为什么这个破碎的程序始终运行?(Why this broken program always run

2019-06-28 01:44发布

我试图从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

更新我加了1000毫秒睡在主线程strating所有线程还是相同的输出之后。 我知道程序是坏了,我希望它表现得这样

Answer 1:

因为这个程序被readynumber应被声明为volatile
由于这样的事实, readynumber是原始变量,对他们的操作都是原子,但不能保证他们会被其他线程可见。
看来,调度运行的线程main ,这就是为什么他们看到的number ,并ready被初始化。 但是,这仅仅是一个调度。
如果添加如sleepmain ,从而影响到调度程序,你会看到不同的结果。
所以,这本书是正确的,也不能保证是否将Runnable S IN单独的线程运行永远不会看到变量的更新,因为变量不是所申报的volatile

更新:
这里的问题是,由于缺乏volatile的编译器是免费阅读领域的ready只有一次,并在循环的每次执行重用的缓存值。
该方案是固有的缺陷。 并与线程的问题,这个问题通常会出现,当你应用程序部署到外地....从JSL :

例如,在下面的(虚线)的代码段中,假定this.done是非易失性布尔字段:

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

编译器是免费阅读领域this.done只有一次,并在循环的每次执行重用的缓存值。 这意味着,循环永远不会终止,即使其他线程改变this.done值。



Answer 2:

重要的是要记住的是,一个破碎的并发程序可能总是与JVM的选择正确的组合,机器架构等等。这并不能使一个好的方案,因为它可能会在不同的上下文无法工作的事实:没有并发的问题显示出来并不意味着没有任何。

换句话说,你不能证明并发程序是测试正确。

回到你的具体的例子,我看到了相同的行为,你的描述。 但是,如果我删除了Thread.yield()在while循环指令,3出的8条线程停止和打印42,但其余的不和程序永远不会结束。



文章来源: Why this broken program always runs?