【问题标题】:Program not terminating after loop completion循环完成后程序未终止
【发布时间】:2020-02-26 07:51:19
【问题描述】:

在以下场景中,布尔值“完成”设置为 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 关键字,除非它是绝对必须的,而且我知道通过将我的值暴露给我不打算暴露的所有线程来工作。

【问题讨论】:

  • 试试private static volatile boolean done;
  • 这能回答你的问题吗? What is the volatile keyword useful for
  • 回答您的编辑的方式:似乎对 Thread.sleep 的调用会导致您的 done 标志的可见性更新,但这似乎是 JVM 实现特定的“功能”你真的不应该依赖。每当您想查看另一个线程写入变量的最新值时,您应该使用 volatile 关键字

标签: java


【解决方案1】:

每个线程都有一个不同的缓存版本done,以提高性能,您的计数器线程忙于计算count,它没有机会重新加载完成。

volatile 确保任何读/写都在主内存上完成,始终更新 cpu 缓存副本。

Thread.sleep 总是暂停当前线程,因此即使 0 您的计数器线程被中断了一段时间 adviced of done 变量变化。

【讨论】:

    【解决方案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 章定义了 内存操作(例如读取和写入)的发生前关系 的共享变量。一个线程写入的结果是 保证只有在写入时才对另一个线程的读取可见 操作发生在读操作之前。同步和 volatile 构造,以及 Thread.start() 和 Thread.join() 方法,可以形成happens-before关系。

    如果您通过线程,它会在执行共享变量的线程时提到“发生在之前”逻辑。所以我的猜测是当你调用 Thread.sleep(0) 时,ma​​in 线程能够正确设置 done 变量,确保它“首先发生”。但是,在多线程环境中,即使这样也不能保证。但由于代码片段非常小,因此可以在这种情况下使用。

    总而言之,我只是运行了您的程序,对变量“done”稍作更改,程序按预期运行:

    private static volatile boolean done;
    

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

    【讨论】:

    • 任何线程都可以随时将done 变量设置为其他值,除非它对其他线程不可见(或者可能在某个时间变得可见,但没有任何保证),除非您使用 volatile 关键字。另外我刚刚发现,任何或多或少的阻塞操作实际上都会导致更改变得可见......例如,通过简单地将 System.out.print() 添加到循环中
    • 是的,我也是。我试着在里面打印一些东西。但它并不总是有效 :P 有时,有时你会注意到它遵循相同的约定并且程序无休止地运行。
    猜你喜欢
    • 2011-10-12
    • 1970-01-01
    • 2014-11-27
    • 2017-01-05
    • 2011-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多