循环结束后程序未终止

时间:2019-10-30 23:46:19

标签: java

在以下情况下,布尔值“ done”被设置为true,这将结束程序。相反,即使while(!done)不再是有效的情况,该程序仍会继续运行,因此它应该已停止。现在,如果我要添加一个线程睡眠,即使睡眠时间为零,程序也会按预期终止。这是为什么?

public class Sample {

    private static boolean done;

    public static void main(String[] args) throws InterruptedException {
        done = false;
        new Thread(() -> {
            System.out.println("Running...");
            int count = 0;
            while (!done) {
                count++;
                try {
                    Thread.sleep(0); // program only ends if I add this line. 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        Thread.sleep(2000);

        done = true; // this is set to true after 2 seconds so program should end.
        System.out.println("Done!"); // this gets printed after 2 seconds
    }

}

编辑:我想了解为什么上面需要Thread.sleep(0)终止。我不想使用volatile关键字,除非它是绝对必须的,并且我确实知道这可以通过将我的值暴露给所有线程(而不是我打算公开的)来起作用。

3 个答案:

答案 0 :(得分:4)

每个线程都为性能创建了不同的 done 缓存版本,您的计数器线程太忙了进行 count 的计算,因此没有机会重新加载< em>完成。

volatile 确保对主内存进行任何读/写操作,并始终更新cpu缓存副本。

Thread.sleep总是暂停当前线程,因此即使计数器线程被中断为<1ms一段时间,也为0,这足以使该线程完成 完成< / em>更改变量。

答案 1 :(得分:2)

我不是Java专家,我什至都不用Java编程,但是让我尝试一下。

stackoverflow上的一个线程解释了Java内存模型:Are static variables shared between threads?

重要部分:https://docs.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility

  

Java语言规范的第17章定义了   发生在存储器操作(如读写)上的事前关系   共享变量。一个线程写的结果是   仅在写入时保证对另一个线程的读取可见   操作发生在读取操作之前。同步和   易失性结构以及Thread.start()和Thread.join()   方法,可以形成先于关系。

如果遍历该线程,则在执行共享变量的线程时会提及“发生于前”逻辑。因此,我的猜测是,当您调用Thread.sleep(0)时,线程能够正确设置完成变量,以确保其“首先发生”。但是,即使在多线程环境中也无法保证。但是由于代码段非常小,因此可以在这种情况下正常工作。

总而言之,我只是在运行您的程序时对变量“ done”进行了少量更改,然后程序按预期工作了:

private static volatile boolean done;

谢谢。也许其他人可以给您更好的解释:P

答案 2 :(得分:0)

Thread.sleep(long)没什么特别的。 Thread.yield()甚至System.out.println()都具有类似的效果。问题是对done变量的访问未同步,因此您依赖于API调用的某些偶然结果来导致线程值更新。为了保证行为,需要进行同步,例如通过volatileAtomicBoolean

public static void main(String[] args) throws InterruptedException {
    AtomicBoolean done = new AtomicBoolean();
    new Thread(() -> {
        System.out.println("Running...");
        while (!done.get()) {}
        System.out.println("Done!"); 
    }).start();

    Thread.sleep(2000);
    done.set(true);
}