Open
Description
通常情况下关于volatile用法的正确例子是这样:
private volatile boolean flag = true;
@org.junit.Test
public void testVolatile() {
new Thread(() -> {
try {
System.out.println("子线程启动");
TimeUnit.SECONDS.sleep(3);
flag = false;
System.out.println("flag false");
} catch (InterruptedException ignore) {
}
}).start();
while (flag) {
}
}
结果也确实如此,不会导致死循环,与之相反如果把volatile去掉:
private boolean flag = true;
的确会死循环,于是我们得出结论:必须使用volatile
保证内存可见性。然而事实并不是如此,下面的例子只在while (flag)
中加入一句无关痛痒的代码:
private boolean flag = true;
@org.junit.Test
public void testVolatile() {
new Thread(() -> {
try {
System.out.println("子线程启动");
TimeUnit.SECONDS.sleep(3);
flag = false;
System.out.println("flag false");
} catch (InterruptedException ignore) {
}
}).start();
while (flag) {
System.out.print(1);
}
}
你会发现,现在也不会死循环了。
其实,在X86平台上,即使不使用memory barrier处理器也会保证变量修改的全局可见。参考:
Does a memory barrier ensure that the cache coherence has been completed?
上面那个例子死循环的原因其实是JVM编译器优化,将代码实际上变成了(加上volatile
或打印阻止了这种优化):
if (flag) {
while (true) {}
}
参考:请问R大 有没有什么工具可以查看正在运行的类的c/汇编代码
那X86既然已经保证了全局可见,为什么我们还要使用volatile
?Java毕竟是跨平台的语言,也许就会运行在不保证全局可见的处理器上,那时就会出错了。
所以如果你没在这种场景下用volatile
,并不是你写的对,而是你恰好把代码跑在了X86上而已。
Metadata
Metadata
Assignees
Labels
No labels