Java 为什么这段代码不像JSR133所建议的那样进入无限循环?

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

在中,它讨论了线程之间动作的可见性-提到了下面的代码示例,它没有使用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");
        }
    }
}

尽管连续执行的结果不同——正如预期的那样——但我不认为它会进入无限循环。我试图理解如何模拟文档中建议的无限循环,我遗漏了什么?声明布尔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字段值的线程将看到写入的值,而不是以前的某个值