0
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;
   }
}

根据“Java Concurrency in Practice”,它可能会打印 0,因为在写入数字之前可能会使读取器线程可以看到写入就绪,或者程序永远不会终止,因为它没有使用足够的同步。不保证主线程写入的 ready 和 number 的值对 reader 线程可见。

这怎么可能?程序将由一个线程按顺序运行,它首先写入数字然后准备好变量。不是吗?这个程序怎么会永远循环呢?

4

2 回答 2

1

无法保证通过在一个线程中更改变量,其他线程将看到这些更改的结果。从技术上讲,它们之间没有发生之前的关系,因此没有保证(虽然在实践中你几乎总是会看到变化)。

这就是线程可能永远运行的原因。

其次,为什么有时 0 ?

好吧,JLS这么说

例如,一个线程中的写入与另一个线程中的读取处于数据竞争中,这些读取可能会出现乱序。

这意味着您的

number = 42;
ready = true;

可以以任何顺序出现。现在它们更有可能按顺序出现,但同样不能保证。

您可以通过将变量更改为易失性来修复它,在这种情况下,写入将始终对读者可见,或者通过将您变异状态的代码设置为关键部分(参见本书)。总的来说,我觉得使用太多 volatile 变量有点小题大做,所以你应该尽量少用它们,比如线程“运行”变量。

public class NoVisibility {
    private static volatile boolean ready;
    private static volatile 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;
    }
}
于 2013-06-18T09:37:19.883 回答
0

如果 ready 未标记为“volatile”,则 ReaderThread 可能会检查其值(为 0)。它稍后不再检查 ready 的值,因为它假设它没有改变。

volatile 关键字指示编译器根本不要有这样的假设,并且变量的值可能会在他的脚下发生变化。

于 2013-06-18T09:47:30.203 回答