Java中线程间可见性错误分析

题目描述

在验证volatile关键字时,发现了这个场景:代码里注释行执行和不执行,最后的结果不一样。

当注释掉打印的信息时,业务逻辑会死循环执行,可以理解

当释放掉注释之后,业务逻辑 就能读取到 子线程修改的值

题目来源及自己的思路

相关代码

public class VolatileTest {

    public static void main(String[] args) {
        VolatileThread thread = new VolatileThread();
        thread.start();

        while (true) {
//            System.out.println("thread.isFlag():" + thread.isFlag());
            if (thread.isFlag()) {
                System.out.println("----------------------");
                break;
            }
        }
    }

    public static class VolatileThread extends Thread {

        public boolean flag = false;

        @Override
        public void run() {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            flag = true;

            System.out.println("flag= " + flag);
        }

        public boolean isFlag() {
            return flag;
        }

        public void setFlag(boolean flag) {
            this.flag = flag;
        }
    }
}

你期待的结果是什么?实际看到的错误信息又是什么?

注释 sout的结果

image.png

执行 sout的结果
image.png

阅读 2.5k
2 个回答

主要原因是执行System.out.println和其他语句相比而言,
非常慢,涉及很多的内部操作, 造成 CPU 缓存失效。所以起的作用相当于让主线程终于有机会看到 flag 发生了变化。

可以加上 count 比较一下(我机器上有输出时循环执行70399次,没有输出时循环执行了801053907次)。

最终多线程共享变量还是要用volatile关键字来处理可见性的问题:

        public  volatile boolean flag = false;

Sout本质是同步执行的,自行看JDK源码就知道了,相当于一种变相的部分同步。

要打印,建议用log4j, logback等日志框架,你会发现注不注释结果就差不多了。

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