为什么此Java程序挂起?

时间:2019-03-13 06:43:50

标签: java

以下程序挂起,但是如果我取消注释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)

2 个答案:

答案 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