以下代码有时会在我的
Windows-PC和Mac上打印“valueWrapper.isZero()”,
两者都以服务器模式运行JVM.
好的,这是因为值字段在ValueWrapper类中不是final,
所以某些线程可能会看到陈旧值为0.
两者都以服务器模式运行JVM.
好的,这是因为值字段在ValueWrapper类中不是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保证的吗?
在规范中简要介绍并不能说服我.
解决方法
我正在解决格雷没有提到的一点,但我会接受他,因为他的回答是正确的
The following code sometimes prints “valueWrapper.isZero()” on my Windows-PC and a Mac,both running their JVM in server mode…. It looks like that now no thread sees a stale value of 0. But is this
guaranteed by the JMM? A brief look in the spec doesn’t convinced me.
你看到valueWrapper.isZero()返回true的原因有时是因为在调用start之后和运行之前valueWrapper正在改变布尔测试.如果您只创建了一个实例,那么Gray总是不会为零.
最终的ValueWrapper valueWrapper = new ValueWrapper(randomInt(10,1024));一直工作是因为字段是本地的线程(和方法),本地对象和匿名内部类的语义是将原始引用复制到类实例中.