《Java并发编程实战》一段代码的疑惑

《Java并发编程实战》3.1节的一段代码:

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

按照书中所讲,这段代码不一定输出42,有可能陷入死循环。原因是代码中没有足够的同步机制,无法保证主线程写入的ready他number对读线程是可见的。也有可能是0,原因是“重排序”,读线程看到了主线程写入的ready却没有看到number。但是经过多次测试,始终输出42,既没有输出0,也没有死循环。为了验证书中所说的主线程修改对读线程不可见,我改写了代码:

public class NoVisibilityLoop {

    private static int number;
    
    private static class ReaderThread extends Thread {
        public void run() {
            while (true) {
                System.out.println(number);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                }
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        new ReaderThread().start();
        Thread.sleep(1000);
        number = 42;
    }
}

多次测试输出结果是:
0
0
0
0
0
42
42
42
42
这个结果说明了主线程写入的number对读线程是可见的。但是这本书的作者一堆大牛,不会搞错的。还请大家帮忙看看我的验证方法哪里出问题了,谢谢。
运行环境:jdk 1.8.0_101,server模式。

阅读 3.7k
5 个回答

原文的意思是让你重复多次运行这段代码
大概运行几十次甚至上百次,可能出现一次死循环,而这个在实际开发中是不可取的

而这是代码设计缺陷的问题,所以建议不要这么编程,要保持主副线程的同步性不要用这样的写法,你继续往后看,这书既然这么说,可能会有别的解决方案来帮你理解他描述的原理

首先测试多次正确也不能保证完全就是正确的,其次这本书是jdk5和6了,jdk8估计做了些优化,

private static boolean ready;
private static int number;

private static class ReaderThread extends Thread {
    @Override
    public void run() {
        int i=0;
        while (!ready) {
            i++;
//                Thread.yield();
        }
        System.out.println(number);
    }
}

public static void main(String[] args) throws Exception {
    new ReaderThread().start();
    TimeUnit.MILLISECONDS .sleep(9);
    number = 42;
    ready = true;
}

把 thread.yield 换成 i++ 或者 sleep 与否 结果都有差别
这种执行顺序是指令重排决定的,不用volatile没法保证

应该是JDK版本差异造成的

新手上路,请多包涵

抄个答案:Because ready isn't marked as volatile and the value may be cached at the start of the while loop because it isn't changed within the while loop. It's one of the ways the jitter optimizes the code.

So it's possible that the thread starts before ready = true and reads ready = false caches that thread-locally and never reads it again.

Check out the volatile keyword.

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题