2

我在“Java Concurrency in Practice”一书中遇到了以下示例。

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 可以打印零,因为在写入 number 之前,ready 的写入可能对 reader 线程可见,这种现象称为重新排序。

我可以理解重新排序问题,但我无法理解可见性问题。为什么ready可能永远不会对读者线程可见?一旦主线程将值写入ready,阅读器线程迟早会有机会运行并且可以读取 的值ready。为什么主线程所做的更改ready可能对阅读器线程不可见?

4

2 回答 2

1

ReaderThreadrun()方法可能永远不会看到 的最新值,ready因为它可以自由假设和优化该值不会在其线程之外更改。这个假设可以通过使用语言的相关并发特性来消除,比如在声明中添加volatile关键字ready

于 2015-07-26T04:52:05.683 回答
1

我相信这是一个新问题,开始出现在多核 CPU 和单独的 CPU 缓存中。

如果您实际上是在读取和修改内存,则无需担心,即使使用多 CPU,您也是安全的,只是每个 CPU 现在都有自己的缓存。内存位置将被缓存,而其他线程将永远不会看到它,因为它将在缓存之外专门运行。

当你让它变得易变时,它会强制两个线程每次都直接进入内存——所以它会减慢速度,但它是线程安全的。

于 2015-07-26T06:30:04.697 回答