我有关于内存可见性的理论问题。以下是示例代码:
@Override
protected void onStop() {
super.onStop();
adapter.stopListening();
}
public class TwoThreadApp {
private static class A {
int x = 1;
}
public static void main(String[] arg) throws InterruptedException {
A a = new A();
Thread t2 = new Thread(() -> {
while (true) {
if (a.x == 2) {
System.out.println(a.x);
return;
}
// IO operation which makes a.x visible to thread "t2"
System.out.println("in loop");
}
});
t2.start();
Thread.sleep(100);
a.x = 2;
}
}
程序无限期地工作,这是预期的行为。 System.out.println("in loop")
总是完成,这是不可预期的,因为a.x不是易失性的,并且没有同步块。我的环境:ubuntu 16.04,openjdk 1.8.0_131
为什么这样做?
答案 0 :(得分:0)
没有System.out.println(“in loop”)程序无限期地工作,这是预期的行为。
相反,该计划应该退出。程序继续运行,这是x
被缓存(非易失性)这一事实的副作用。缓存是编译器的最优化,您不应该依赖它(取决于JVM设置)。
但是使用System.out.println(“in loop”)它总是完成,这是不期望的,因为a.x不是易失性的,并且没有同步块。
这是恕我直言的预期行为。我不能告诉你为什么,我会假设IO操作涉及清除线程缓存(如果有人有更好的洞察力,请评论/纠正)。
在没有同步或锁定的情况下访问变量或使用多个线程进行易失性可能实际上是不可预测的。
您甚至可以使用-Djava.compiler=NONE
禁用许多优化(然后程序应始终按预期退出)
答案 1 :(得分:0)
恕我直言,这与我认为的编译器优化有关。在您的第一个示例中,编译可以决定将If条件移出while循环,如
if(a.x == 2) while(true) {...}
在第二种情况下,当您使用println时,内部使用synchronized关键字编译器可能无法优化上述代码。
这就是我的想法,也许我错了。
修改:您也可以在此处参考:Loop doesn't see changed value without a print statement