Java 为什么这段代码不像JSR133所建议的那样进入无限循环?
在中,它讨论了线程之间动作的可见性-提到了下面的代码示例,它没有使用volatile关键字作为布尔字段,如果两个线程运行它,它可能会成为一个无限循环。以下是JSR中的代码:Java 为什么这段代码不像JSR133所建议的那样进入无限循环?,java,multithreading,concurrency,volatile,java-memory-model,Java,Multithreading,Concurrency,Volatile,Java Memory Model,在中,它讨论了线程之间动作的可见性-提到了下面的代码示例,它没有使用volatile关键字作为布尔字段,如果两个线程运行它,它可能会成为一个无限循环。以下是JSR中的代码: class LoopMayNeverEnd { boolean done = false; void work() { while (!done) { // do work } } void stopWork() { do
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");
}
}
}
尽管连续执行的结果不同——正如预期的那样——但我不认为它会进入无限循环。我试图理解如何模拟文档中建议的无限循环,我遗漏了什么?声明布尔volatile如何删除无限循环?实际行为是操作系统和JVM特有的。例如,默认情况下,Java在32位Windows上以客户端模式运行,在Mac上以服务器模式运行。在客户端模式下将终止
工作
方法,但不会在服务器模式下终止
这是因为Java服务器JIT编译器进行了优化。JIT编译器可能会优化while循环,因为它不会看到变量done
在线程上下文中发生更改。无限循环的另一个原因可能是,一个线程可能最终从其寄存器或缓存中读取标志值,而不是进入内存。因此,它可能永远不会看到另一个线程对此标志所做的更改
本质上,通过添加volatile
可以使线程拥有done
标志不缓存该标志。因此,布尔值
存储在公共内存中,因此保证了可见性。另外,通过使用volatile
可以禁用可以内联标志值的JIT优化
基本上,如果您想重现无限循环,只需在服务器模式下运行您的程序:
java -server VolatileTest
删除println语句,您可能会有一个永不停止的程序。但即使你没有,记住,仅仅因为某些事情可能发生,即使是百万分之一次,并不意味着它总是会发生。它可能取决于您的JVM、操作系统等。您无法保证线程会看到新的布尔值。这与说“你保证线程不会看到它”非常不同。谢谢你的评论。删除while循环中的print语句足以使其进入无限循环。。。现在程序打印“停止工作”,然后永不结束。布尔变量的赋值到哪里去了?它去了寄存器,或者停止线程使用的CPU核心的内存缓存,甚至是主内存。但是读取线程可以继续从自己的CPU核心内存缓存中读取,因为该字段没有标记为volatile。这是有道理的。最后一个问题,将布尔值声明为volatile是否意味着由于“发生在之前”关系,stopWork()总是首先被调用?不,一点也不。它保证,如果一个线程已经向volatile字段写入了一个值,那么另一个读取volatile字段值的线程将看到写入的值,而不是以前的某个值