我正在看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;
}
}
发布于 2009-12-17 12:32:01
因为ready
没有被标记为volatile
,并且该值可能会在while
循环开始时被缓存,因为它在while
循环中没有更改。这是抖动优化代码的方法之一。
因此,线程有可能在ready = true
之前启动,并读取ready = false
本地缓存该线程,然后再也不读取它。
发布于 2009-12-17 12:40:43
Java Memory Model允许Java虚拟机优化引用访问,例如,如果它是单线程应用程序,除非字段被标记为volatile
或访问带有锁(实际上,使用锁的情况会变得有点复杂)。
在您提供的示例中,JVM可以推断ready
字段不能在当前线程中修改,因此它将用false
替换!ready
,从而导致无限循环。将字段标记为volatile
将导致JVM每次都检查字段值(或者至少确保ready
更改传播到正在运行的线程)。
发布于 2010-02-21 08:31:47
问题的根源在于硬件--每个CPU在缓存一致性、内存可见性和操作的重新排序方面都有不同的行为。Java在这一点上比C++更好,因为它定义了一个所有程序员都可以信赖的跨平台内存模型。当Java运行在其内存模型弱于Java内存模型所要求的内存模型的系统上时,JVM必须弥补这一差异。
像C这样的语言“继承”了底层硬件的内存模型。正在努力为C++提供一个正式的内存模型,以便C++程序可以在不同的平台上表示相同的事情。
https://stackoverflow.com/questions/1919469
复制相似问题