1. 程式人生 > 程式設計 >Java併發volatile可見性的驗證實現

Java併發volatile可見性的驗證實現

普通讀 無法及時獲得 主記憶體變數

public class volatileTest {
  static boolean flag = false;//非volatile變數

  public static void main(String[] args) throws Exception {
    new Thread(new Runnable() {
      @Override
      public void run() {
        while(!flag){
        };
      }
    }).start();
    Thread.sleep(100);
    flag = true;
    System.out.println("主執行緒執行完畢");
  }
}

主執行緒已經修改了flag為true,但子執行緒一直不會退出迴圈,因為子執行緒一直沒有同步到 主記憶體中的變數的值。

Java併發volatile可見性的驗證實現

截圖可見程式一直沒有退出,使用dump threads後:

"Thread-0" #12 prio=5 os_prio=0 tid=0x0000000022d89800 nid=0x168 runnable [0x00000000248df000]
java.lang.Thread.State: RUNNABLE
at volatileTest$1.run(volatileTest.java:10)
at java.lang.Thread.run(Thread.java:745)

volatile讀 及時獲得 主記憶體變數

public class volatileTest {
  static volatile boolean flag = false;//volatile變數

  public static void main(String[] args) throws Exception {
    new Thread(new Runnable() {
      @Override
      public void run() {
        while(!flag){
        };
      }
    }).start();
    Thread.sleep(100);
    flag = true;
    System.out.println("主執行緒執行完畢");
  }
}

加了一個volatile關鍵字,子執行緒就能檢測到flag的變化了。子執行緒會退出。

普通讀+sleep

public class volatileTest {
  static boolean flag = false;//非volatile變數

  public static void main(String[] args) throws Exception {
    new Thread(new Runnable() {
      @Override
      public void run() {
        while(!flag){
          try {
            Thread.sleep(1);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        };
      }
    }).start();
    Thread.sleep(100);
    flag = true;
    System.out.println("主執行緒執行完畢");
  }
}

加了sleep,子執行緒會退出。

其實就算變數不是volatile的,JVM也會盡量去保證可見性。最開始的例子,由於CPU一直執行迴圈,沒有其他時間來獲取 主記憶體中的變數的最新值,但加了sleep後,CPU就有時間去獲取 主記憶體中的東西了。

普通讀+同步塊

public class volatileTest {
  static boolean flag = false;//非volatile變數
  static Object sync = new Object();

  public static void main(String[] args) throws Exception {
    new Thread(new Runnable() {
      @Override
      public void run() {
        while(!flag){
          synchronized (sync) {}//隨便synchronized一個物件
          //synchronized (this)也可以
        };
      }
    }).start();
    Thread.sleep(100);
    flag = true;
    System.out.println("主執行緒執行完畢");
  }
}

加了同步塊,子執行緒會退出。

這是因為synchronized具體過程是:

  • 獲得同步鎖;
  • 清空工作記憶體;
  • 從主記憶體拷貝物件副本到工作記憶體;
  • 執行程式碼(計算或者輸出等);
  • 重新整理主記憶體資料;
  • 釋放同步鎖。

簡單的說,synchronized進入時,會將 主記憶體中最新的變數,拷貝進 自己執行緒 的工作記憶體。synchronized退出時,會把 自己執行緒的工作記憶體的變數 弄進 主記憶體中。

同步塊 遭遇 鎖消除

public class volatileTest {
  static boolean flag = false;//非volatile變數

  public static void main(String[] args) throws Exception {
    new Thread(new Runnable() {
      @Override
      public void run() {
        while(!flag){
          synchronized (new Object()){}
        };
      }
    }).start();
    Thread.sleep(100);
    flag = true;
    System.out.println("主執行緒執行完畢");
  }
}

子執行緒不會退出。

原因是:synchronized (new Object()){}中這個Object永遠不可能逃逸到同步塊以外去,所以同步操作其實根本不需要執行了,既然沒有執行同步,那麼相當於這裡是啥也沒有。

普通讀+System.out.println

public class volatileTest {
  static boolean flag = false;

  public static void main(String[] args) throws Exception {
    new Thread(new Runnable() {
      @Override
      public void run() {
        while(!flag){
          System.out.println("子執行緒running");
        };
      }
    }).start();
    Thread.sleep(100);
    flag = true;
    System.out.println("主執行緒執行完畢");
  }
}

加了System.out.println,子執行緒會退出。

因為out這個PrintStream例項的println實現是:

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

因為它也有同步塊。

到此這篇關於Java併發volatile可見性的驗證實現的文章就介紹到這了,更多相關Java併發volatile驗證內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!