以下程序挂起,但如果我取消注释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)
我认为这是因为值可能会被复制到CPU缓存中,这意味着多个线程可能会为同一个变量设置不同的值。为了防止这种情况,您可以告诉java使用主内存。请将“public Integer val”更改为“public volatile Integer val”,并观察该程序现在退出。
更多关于挥发性的信息:When to use volatile with multi threading?
当你问为什么会发生这种情况时:多线程是一种你应该假设发生的事情是随机的。我的猜测是printf中的线程等待一些IO资源,等待时它被挂起,但是当它恢复时,缓存会先刷新。但正如我刚才所说,这只是一个猜测,因为你不知道什么时候进行不同步的线程操作
解决方案显示在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