程序在循环完成后未终止

问题描述 投票:4回答:3

在以下情况下,布尔值'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关键字,除非它是绝对必须的,并且我确实知道这可以通过将我的值暴露给所有我不想公开的线程来起作用。

java
3个回答
1
投票

我不是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)时,main线程能够正确设置完成变量,以确保其“首先发生”。但是,即使在多线程环境中也无法保证。但是由于代码段非常小,因此可以在这种情况下使用。

总结起来,我只是在运行您的程序的同时对变量“ done”进行了微小的更改,程序按预期运行:

private static volatile boolean done;

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


1
投票

每个线程具有为性能而创建的done的不同缓存版本,您的计数器线程太忙于进行count的计算,因此没有机会重新加载done

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

Thread.sleep总是暂停当前线程,因此,即使计数器线程为0中断了<1ms,这也足以使线程<>更改变量[[adviced]]。] >

Thread.sleep(long)没有什么特别的。 Thread.yield()甚至System.out.println()也有类似的效果。问题是对done变量的访问未同步,因此您依赖API调用的某些
incidental结果导致线程上的值更新。为了保证行为,需要进行同步,例如通过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); }


0
投票
incidental结果导致线程上的值更新。为了保证行为,需要进行同步,例如通过volatileAtomicBoolean
© www.soinside.com 2019 - 2024. All rights reserved.