【发布时间】:2019-08-14 19:09:50
【问题描述】:
在JSR-133 section 3.1 中,讨论了线程之间操作的可见性 - 提到下面的代码示例,它没有为布尔字段使用 volatile 关键字,如果两个线程正在运行它,它可能会变成一个无限循环。这是来自 JSR 的代码:
class LoopMayNeverEnd {
boolean done = false;
void work() {
while (!done) {
// do work
}
}
void stopWork() {
done = true;
}
}
这是我感兴趣的部分中重要部分的引述:
... 现在想象一下,创建了两个线程,其中一个 线程调用work(),在某个时候,另一个线程调用stopWork()。因为有 两个线程之间没有发生之前的关系,循环中的线程可能永远不会 查看其他线程执行的更新完成...
这是我自己编写的 Java 代码,以便我可以看到它的循环:
public class VolatileTest {
private boolean done = false;
public static void main(String[] args) {
VolatileTest volatileTest = new VolatileTest();
volatileTest.runTest();
}
private void runTest() {
Thread t1 = new Thread(() -> work());
Thread t2 = new Thread(() -> stopWork());
t1.start();
t2.start();
}
private void stopWork() {
done = true;
System.out.println("stopped work");
}
private void work() {
while(!done){
System.out.println("started work");
}
}
}
尽管连续执行的结果不同 - 正如预期的那样 - 我看不到它会进入无限循环。我试图了解如何模拟文档建议的无限循环,我错过了什么?声明boolean volatile,如何去掉无限循环?
【问题讨论】:
-
删除您的 println 语句,您可能会拥有一个永不停止的程序。但即使你不这样做,请记住,仅仅因为某件事可能发生,即使百万分之一次,并不意味着它会一直发生。它可能取决于您的 JVM、您的操作系统等。您只是无法保证线程会看到新的布尔值。这与说“保证线程不会看到它”有很大不同。
-
感谢您的评论。删除 while 循环中的 print 语句足以使其进入无限循环......现在程序打印“停止工作”然后永远不会结束。对布尔变量 true 的赋值到哪里去了?
-
它去了寄存器,或者是停止线程使用的CPU核心的内存缓存,甚至是主内存。但是读取线程可以继续从自己的 CPU 核心内存缓存中读取,因为该字段没有被标记为 volatile。
-
这是有道理的。最后一个问题,将布尔值声明为 volatile 是否意味着 stopWork() 由于发生前的关系而总是首先被调用?
-
不,一点也不。它保证如果一个线程向 volatile 字段写入了一个值,那么另一个读取 volatile 字段的值的线程将看到写入的值,而不是之前的某个值。
标签: java multithreading concurrency volatile java-memory-model