我正在查看Brian Goetz撰写的"Java Concurrency in Practice"中的代码示例.他说这段代码可能会保持无限循环,因为"'ready'的值可能永远不会对读者线程可见".我不明白这是怎么发生的......
public class NoVisibility {
private static boolean ready;
private static int number;
private static class ReaderThread extends Thread {
public void run() {
while (!ready)
Thread.yield();
System.out.println(number);
}
}
public static void main(String[] args) {
new ReaderThread().start();
number = 42;
ready = true;
}
}
Run Code Online (Sandbox Code Playgroud)
Ric*_*ler 30
因为ready没有标记为volatile,并且值可能在while循环开始时被缓存,因为它在while循环内没有更改.这是抖动优化代码的方式之一.
因此,线程可能在之前启动ready = true并读取ready = false线程本地的缓存,而不会再次读取它.
查看volatile关键字.
原因在代码示例之后的部分中进行了解释.
3.1.1陈旧数据
NoVisibility展示了不充分同步的程序可能导致令人惊讶的结果的方式:陈旧的数据.当读者线程检查时ready,它可能会看到过时的值.除非每次访问变量时都使用同步,否则可能会看到该变量的陈旧值.
Java内存模型允许JVM优化引用访问,例如,如果它是单线程应用程序,除非字段被标记为volatile或具有锁定的访问(实际上故事有点复杂).
在您提供的示例中,JVM可以推断该ready字段可能未在当前线程中被修改,因此它将替换!ready为false,从而导致无限循环.将字段标记为volatile将导致JVM每次都检查字段值(或者至少确保ready更改传播到正在运行的线程).