以下代码有时会在Windows PC和Mac上打印“valueWrapper.isZero()”,
两者都在服务器模式下运行其JVM。
好的,这是因为ValueWrapper类中的value字段不是final,
因此某个线程看到过时值0的可能性很大。

public class ConcurrencyApp {
    private final Random rand = new Random(System.currentTimeMillis());
    private ValueWrapper valueWrapper;

    private static class ValueWrapper {
       private int value;

        public ValueWrapper(int value) {
            this.value = value;
        }

        public boolean isZero() {
            return value == 0;
        }
    }

    private void go() {
        while (true) {
            valueWrapper = new ValueWrapper(randomInt(10, 1024));
            Thread thread = new Thread(new Runnable() {

                @Override
                public void run() {
                    if (valueWrapper.isZero()) {
                        System.out.println("valueWrapper.isZero()");
                    }
                }
            });
            thread.start();
        }
    }

    private int randomInt(int min, int max) {
        int randomNum = rand.nextInt((max - min) + 1) + min;
        return randomNum;
    }

    public static void printVMInfos() {
        String vmName = System.getProperty("java.vm.name");
        System.out.println("vm name: " + vmName);
        int cores = Runtime.getRuntime().availableProcessors();
        System.out.println("available cores: " + cores);
    }

    public static void main(String[] args) {
        ConcurrencyApp app = new ConcurrencyApp();
        printVMInfos();
        app.go();
    }
}

但是下面的修改呢,我在这里使用了一个局部最终变量:
private void go() {
    while (true) {
        final ValueWrapper valueWrapper = new ValueWrapper(randomInt(10, 1024));
        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                if (valueWrapper.isZero()) {
                    System.out.println("valueWrapper.isZero()");
                }
            }
        });

        thread.start();
    }
}

看起来现在没有线程看到过时的值0。
但这是JMM保证的吗?
规范中的简短外观并不能说服我。

最佳答案

我要谈的是格雷没有提出的观点,但我会接受他的观点,因为他的回答是正确的



您之所以看到valueWrapper.isZero()有时返回true的原因有时是因为valueWrapper在调用start之后且在run进入 boolean 测试之前发生了变化。如果您仅创建了一个实例,那么它将永远不会为零(如Gray所述)。
final ValueWrapper valueWrapper = new ValueWrapper(randomInt(10, 1024));一直有效的原因是因为该字段是线程(和方法)本地的,而本地对象和匿名内部类的语义是将原始引用复制到类实例中。

09-26 12:55