为什么这个java程序挂起?
以下程序挂起,但如果我取消对为什么这个java程序挂起?,java,Java,以下程序挂起,但如果我取消对System.out.printf的注释,它将正常退出。我想知道为什么会这样。提前谢谢 public class MainClass{ public static class Node{ public Integer val; Node() { val = new Integer(0); } public void hang() { int i
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版本的输出
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”,并观察程序现在是否退出 有关volatile的更多信息,请参见:
正如您所问的,为什么会发生这种情况:多线程是一种你应该假设发生的事情是随机的东西。我的猜测是printf处的线程在等待某个IO资源,等待时它将挂起,但当它恢复时,缓存将首先刷新。但正如我刚才所说,这只是一个猜测,因为您永远无法确定在执行未同步的线程操作时,解决方案如maslan的回答所示。但是,对于您的问题,我认为添加'System.out.printf'将停止挂起的原因是,如果没有,JIT编译器将转换代码,如下所示:
if (this.val != 1) while(true) {i++;}
我猜添加调用将停止JIT进行优化。因此,当线程决定从内存中读取而不使用缓存时,您的程序会正常退出
我猜测的一个证据是,通过向VM选项添加'-XX:loopOptCount=0',您的原始程序(不带printf)将正常退出
在字段声明中添加'volatile'关键字也会停止转换,请参见也许您是对的。但是当我们使用
System.out.printf(“”)时,为什么它不挂起代码>在while循环中?@NicholasK它只是一个缓存,你不知道何时以及如何刷新它。这就像使用BufferedWriter而不在最后关闭writer一样:您完全不知道缓冲区中还有多少文本,最后有多少文本将最终进入文件,以及缓冲区中还有多少文本。(显然,如果您使用Java7并尝试使用资源,您将永远看不到这一点)。可能写入控制台的额外时间导致缓存刷新,可能线程在该操作期间挂起,等待IO资源,当重新启动以继续时,缓存会提前刷新printf
中断循环,因为它在后台使用同步,为了防止对同一个内部文件描述符的两次调用(还有什么会产生未定义的行为)@Ferrybig我仍然确信这只是一个猜测,但感谢你让我相信我的想法:)我想指出,我们都只是猜测,所以没有人会把我们的两个答案都当作100%肯定的解释,多线程婴儿;)是,通过添加-XX:LoopOptCount=0
程序将正常退出。但是有没有办法直接证明你的猜测?我使用hsdis来查看汇编代码,但没有看到您上面提到的这种结构。@Charles添加-XX:+PrintOptoAssembly
来显示优化的汇编如何?@CGBCBC还没有调试构建jvm。我稍后再试试。