java实例变量对其他线程不可见

时间:2018-04-21 00:11:52

标签: java multithreading concurrency thread-safety java-threads

我在书中遇到过这段代码。它声明 NoVisibility可以永远循环,因为ready的值可能永远不会变为 读者线程可见。

我对此声明感到困惑。为了使循环永远运行,ready必须始终为false,这是默认值。这意味着它必须在执行ready = true;时失败,因为读者线程将始终从内存中读取ready变量。赋值发生在CPU中,它必须在将数据刷回主内存时遇到一些问题。我想我需要一些关于它如何失败的情况的解释,或者我可能错过了其他一些部分。

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

2 个答案:

答案 0 :(得分:2)

你的理解是有缺陷的。您假设 Java将在此处直观地表现。事实上,它可能不会。事实上,如果您不遵守规则,Java语言规范允许非直观行为。

更具体地说,在您的示例中,不保证第二个线程将看到第一个线程分配给ready 1 <的结果 / SUP>。这是由于:

  1. 编译器将ready的值缓存在第一个或第二个线程的寄存器中。
  2. 编译器不包括强制写入从一个内核的内存缓存刷新到主内存或类似内容的指令。
  3. 如果要保证第二个线程将看到写入的结果,那么两个线程对ready的读取和写入必须(正确)同步,或者ready变量必须被宣布为不稳定。

    所以......

      

    这意味着它必须在执行ready = true;时失败,因为读者线程将始终从内存中读取ready变量。

    不正确。 &#34;因为&#34;本例中的Java语言规范不保证。

    是。这不直观。根据您对单线程程序的理解,依靠您的直觉是不可靠的。

    这本书是对的。

    1 - 它可能会立即看到结果,或者在短暂或长时间延迟之后看到结果。或者它可能从不看到它们。而且,从一个系统到另一个系统以及Java平台的版本,行为可能会有所不同。所以你的程序(幸运的是)在一个系统上一直运行可能并不总能在另一个系统上运行。

答案 1 :(得分:1)

ready的值可能会更新,但另一个线程可能永远不会知道它。那里你需要易变的变量!一个线程假定该变量仅由此且仅由该线程使用。因此,它从它创建的堆栈中读取其值。

private static volatile boolean ready;

它告诉你的程序从内存中而不是从堆栈中准备好了什么volatile。

实际上jvm的作用是翻译:

while(flag){...}

要:

if(flag){
    while(true){
}

创建线程时会创建堆栈。它收集变量的值以便以后使用它们。

这是我所理解的,如果我错了,请纠正我!