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 影響了內存的可見性。


技 術 無 他, 唯 有 熟 爾。
知 其 然, 也 知 其 所 以 然。
踏 實 一 些, 不 要 着 急, 你 想 要 的 歲 月 都 會 給 你。


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