在以下情况下,布尔值“ 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关键字,除非它是绝对必须的,并且我确实知道这可以通过将我的值暴露给所有线程(而不是我打算公开的)来起作用。
答案 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调用的某些偶然结果来导致线程值更新。为了保证行为,需要进行同步,例如通过volatile
或AtomicBoolean。
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);
}