与System.out关联的Java线程的奇怪行为

Hum*_*mad 5 java multithreading race-condition

我有一个简单的TestThreadClientMode类来测试竞争条件.我尝试了两次尝试:

  1. 当我System.out.println(count);在第二个线程中使用注释运行以下代码时,输出为:

OS: Windows 8.1 flag done set true ...

第二个线程永远活着.因为第二个线程永远不会看到done由主线程设置为true 的标志的更改.

  1. 当我取消注释System.out.println(count);输出时:

    OS: Windows 8.1 0 ... 190785 190786 flag done set true Done! Thread-0 true

程序在1秒后停止.

怎么System.out.println(count);让第二个线程看到变化done

public class TestThreadClientMode {
    private static boolean done;
    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            public void run() {
                int count = 0;
                while (!done) {
                    count ++;
                    //System.out.println(count);
                }
                System.out.println("Done! " + Thread.currentThread().getName() + "  " + done);
            }
        }).start();
        System.out.println("OS: " + System.getProperty("os.name"));

        Thread.sleep(1000);
        done = true;

        System.out.println("flag done set true ");
    }
}
Run Code Online (Sandbox Code Playgroud)

Par*_*yes 5

这是内存一致性错误的一个很好的例子.简单地说,变量已更新,但第一个线程并不总是看到变量变化.这个问题可以通过声明done变量来解决volatile:

private static volatile boolean done;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,对变量的更改对所有线程都是可见的,程序总是在一秒后终止.

更新:似乎使用System.out.println确实解决了内存一致性问题 - 这是因为print函数使用了实现同步的底层流.同步建立了一个发生在之前的关系,如我链接的教程中所述,它与volatile变量具有相同的效果.(这个答案的详细信息.也可以归功于@Chris K指出流操作的副作用.)