Java中System.out.println()为何会影响内存可见性

本文主要说明System.out.println为啥会影响内存的可见性?

先看示例代码:

public class T implements Runnable {
    private boolean flag = false;

    public boolean getFlag() {
        return flag;
    }

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

    @Override
    public void run() {
        while (true){
            if(getFlag()){
                System.out.println(flag);
                System.out.println("Yeah!!!");
                break;
            }
        }
    }
}

这是一个实现了 Runnable 的自定义线程类,里面的 run 方法也很简单,它用 while 循环去检查 flag 变量是否为 true,如果为true,就打印字符,并且退出循环。

下面启动线程:

public class A {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();

        Thread thread = new Thread(t);
        thread.start();

        Thread.sleep(2000);

        t.setFlag(true);
    }
}

现在在另外一个类的 main 方法中去启动线程,然后等待2秒后将 flag 变量修改为 true。

这段代码一看就是有问题的,线程类T的run方法会一直循环下去,它感知不到 main 方法里面已经将 flag设置成了 true。
原因是这里存在两个线程,一个是线程T,一个是main所在的主线程。这两个线程的数据是不会相互干扰的。
也就是说这两个线程里面都存在一个flag的副本。真正的 flag 这个变量存放在主内存中,两个线程的工作内存中的 flag 变量只是副本。

这样理解没问题。因为我就是要验证这个现象。

但是接下来就会出现一个奇怪的现象!!!!

我先稍微的改动一下线程类T里面的run方法,我在里面加一句打印语句,改完之后如下所示:

@Override
 public void run() {
      while (true){
   		// 新增的打印
        System.out.println(111);
        if(getFlag()){
            System.out.println(flag);
            System.out.println("Yeah!!!");
            break;
        }
      }
 }

然后我现在再去运行main方法,却惊奇的发现if里面的语句打印出来了,线程T感知到了主线程对flag的改动。这是为什么?

在这里插入图片描述

线程T感知到了主线程对flag的改动,说明这条打印语句已经影响到了内存可见性。可是他是如何影响的呢?

答案就在源码里面:

 public void println(String x) {
	    synchronized (this) {
	        print(x);
	        newLine();
	    }
 }

原来println有一个上锁的操作。

使用了 synchronized 上锁会做以下操作:

  1. 获得同步锁;
  2. 清空工作内存;
  3. 从主内存拷贝对象副本到工作内存;
  4. 执行代码(计算或者输出等);
  5. 刷新主内存数据;
  6. 释放同步锁。

总结

这下明白了,为什么 System.out.println()方法会影响内存可见性了。
说得更细一点就是 System.out.println()方法中的 synchronized 影响了内存的可见性。


技 术 无 他, 唯 有 熟 尔。
知 其 然, 也 知 其 所 以 然。
踏 实 一 些, 不 要 着 急, 你 想 要 的 岁 月 都 会 给 你。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章