以下程序挂起,但是如果我取消注释System.out.printf
,它将正常退出。我不知道为什么会这样。预先感谢。
public class MainClass{
public static class Node{
public Integer val;
Node() {
val = new Integer(0);
}
public void hang() {
int i = 0;
while(this.val != 1) {
// the program quit normally if I uncomment the following line
//System.out.printf("");
i++;
}
System.out.println(i);
System.out.println("quit");
}
private int func(int i) {
return i+1;
}
}
public static void main(String[] args) throws InterruptedException{
Node n = new Node();
Thread t = new Thread(new Runnable(){
@Override
public void run() {
n.hang();
}
});
t.start();
// wait for t to start
Thread.sleep(500);
n.val = new Integer(1);
Thread.sleep(500);
t.join();
}
}
java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-8u191-b12-0ubuntu0.16.04.1-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)
答案 0 :(得分:5)
我认为这是因为这些值可能会被复制到CPU缓存中,这意味着多个线程可能为同一变量设置不同的值。为了防止这种情况,您可以告诉Java使用主内存。请将“ public Integer val”更改为“ public volatile Integer val”,并观察程序已退出。
更多关于挥发性的信息: When to use volatile with multi threading?
当您问为什么会发生这种情况时:多线程是一种东西,您应该假设发生的事情是随机的。我的猜测是,printf的线程等待一些IO资源,并且在等待时将其挂起,但在恢复时将首先刷新缓存。但是正如我刚才所说,这只是一个猜测,因为您不确定执行不同步的线程操作时会
答案 1 :(得分:1)
解决方案显示在maslan的答案中。但是,关于您的问题,我认为添加'System.out.printf'可以停止挂起的原因是,如果没有,JIT编译器将转换如下代码:
if (this.val != 1) while(true) {i++;}
我猜想添加呼叫将停止JIT进行该优化。因此,当线程决定从内存中读取而不使用缓存时,您的程序将正常退出。
我猜测的一个证明是,通过在VM选项中添加'-XX:LoopOptsCount = 0',您的原始程序(没有printf)可以正常退出。
在字段声明中添加“ volatile”关键字也将停止转换,请参见When Java refresh Thread Cache to actual copy