2

我正在尝试来自 JCIP 的示例,并且下面的程序不应该工作,但即使我执行它说 20 次它总是工作,这意味着ready并且number正在变得可见,即使它应该在这种情况下

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 睡眠仍然相同的输出。我知道程序坏了我希望它会这样

4

2 回答 2

3

该程序已损坏readynumber应声明为volatile.
由于readynumber是原始变量,对它们的操作是原子的,不能保证它们对其他线程可见。
调度程序似乎在之后 main运行线程,这就是他们看到并被初始化的number原因ready。但这只是一种安排。
如果您添加例如一个sleepinmain以影响调度程序,您将看到不同的结果。
所以这本书是正确的,不能保证是否Runnable在单独的线程中运行的 s 将永远看到变量正在更新,因为变量未声明为volatile.

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

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

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

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

于 2012-09-09T17:28:40.243 回答
1

需要记住的重要一点是,一个损坏的并发程序可能总是与 JVM 的选项、机器架构等的正确组合一起工作。这并不能使它成为一个好的程序,因为它可能会在不同的上下文中失败:事实上没有出现并发问题并不意味着没有。

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

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

于 2012-09-09T20:10:53.830 回答