我在“实践中的Java并发”一书中遇到了以下示例。
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;
}
}
进一步说明:
NoVisibility可能永远循环,因为ready的值可能永远不会变为 读者线程可见。更奇怪的是,NoVisibility可以打印出来 为零,因为写入就绪可能会在之前对读者线程可见 写入数字,这种现象称为重新排序。
我可以理解重新排序问题,但我无法理解可见性问题。为什么ready
的值可能永远不会对读者线程可见?一旦主线程在ready
中写入值,迟早的读者线程将有机会运行,它可以读取ready
的值。为什么ready
中主线程所做的更改可能对读者线程不可见?
答案 0 :(得分:1)
ReaderThread
' run()
方法可能永远不会看到ready
的最新值,因为它可以自由地假设并优化该值不会在它的线程。可以通过使用该语言的相关并发功能(例如将volatile
添加到ready
的声明来删除此假设。
答案 1 :(得分:1)
我认为这是一个新的问题,它开始出现在多核CPU和独立的CPU缓存中。
如果您实际上正在阅读和修改内存,则无需担心,即使使用多CPU,您也可以安全,除非每个CPU现在都拥有自己的缓存。内存位置将被缓存,而另一个线程将永远不会看到它,因为它将仅在缓存中运行。
当你使它变得不稳定时,它会强制两个线程每次都直接进入内存 - 所以它会使事情变慢,但它的线程是安全的。