为什么这个破碎的程序总是运行

时间:2012-09-09 17:18:26

标签: java multithreading static thread-safety

我正在尝试来自JCIP的示例,而且程序不应该有效,但即使我执行了20次它总是有效,这意味着readynumber变得可见,即使在这种情况下应该

public class NoVisibility {
private static boolean ready;
private static int number;

private static class ReaderThread implements Runnable {
    public void run() {
        while (!ready)
            Thread.yield();
        System.out.println(number);
    }
}

public static void main(String[] args) {
    System.out.println(Runtime.getRuntime().availableProcessors());
    //Number of Processor is 4 so 4+1 threads
    new Thread(new ReaderThread()).start();
    new Thread(new ReaderThread()).start();
    new Thread(new ReaderThread()).start();
    new Thread(new ReaderThread()).start();
    new Thread(new ReaderThread()).start();

    number = 42;
    ready = true;
}
}

在我的机器上它始终打印

4 -- Number of Processors
42
42
42
42
42

According to Listing 3.1 of JCIP It should sometimes print 0 or should never terminate it also suggest that there is no gaurantee that ready and number written by main thread will be visible to reader thread

更新 在说明所有线程仍然相同的输出后,我在主线程中添加了1000ms睡眠。我知道程序已经坏了我希望它的行为方式

2 个答案:

答案 0 :(得分:3)

此程序已被删除,因为readynumber应声明为volatile
由于readynumber是原始变量这一事实,对它们的操作是原子但是保证它们可见其他线程。
似乎调度程序在 main之后运行线程,这就是他们看到numberready被初始化的原因。但那只是一个安排 如果您添加例如sleep中的main以便影响调度程序,您会看到不同的结果 所以这本书是正确的,不能保证在单独的线程中运行的Runnable是否会看到变量被更新,因为变量未被声明为volatile

<强>更新
这里的问题是由于缺少volatile编译器,只需一次读取字段ready,并在循环的每次执行中重用缓存值。
该计划本质上存在缺陷。并且通过线程问题,当您将应用程序部署到现场时,通常会出现问题.... 来自JSL

  

例如,在下面(破碎的)代码片段中,假设   this.done是一个非易失性布尔字段:

     

while(!this.done)
      了Thread.sleep(1000);

     

编译器可以自由阅读this.done字段一次,并重用   每次执行循环时的缓存值。这意味着   循环永远不会终止,即使其他线程改变了   this.done的价值。

答案 1 :(得分:1)

重要的是要记住,一个破碎的并发程序可能总是适用于JVM的选项,机器架构等的正确组合。这并不能使它成为一个好的程序,因为它可能会失败一个不同的背景:没有出现并发问题的事实并不意味着没有。

换句话说,您无法通过测试证明并发程序是正确的。

回到您的具体示例,我看到的行为与您描述的行为相同。但是,如果我删除while循环中的Thread.yield()指令,则8个线程中的3个停止并打印42,但其余的不会,程序永远不会结束。