神奇的Volatile關鍵字

volatile關鍵字的作用、原理

在只有雙重檢查鎖,沒有volatile的懶加載單例模式中,由於指令重排序的問題,我確實不會拿到兩個不同的單例了,但我會拿到“半個”單例

而發揮神奇作用的volatile,可以當之無愧的被稱爲Java併發編程中“出現頻率最高的關鍵字”,常用於保持內存可見性和防止指令重排序。

保持內存可見性

內存可見性(Memory Visibility):所有線程都能看到共享內存的最新狀態。

失效數據

以下是一個簡單的可變整數類:

public class MutableInteger {
    private int value;
    public int get(){
        return value;
    }
    public void set(int value){
        this.value = value;
    }
}

MutableInteger不是線程安全的,因爲getset方法都是在沒有同步的情況下進行的。如果線程1調用了set方法,那麼正在調用的get的線程2可能會看到更新後的value值,也可能看不到

解決方法很簡單,將value聲明爲volatile變量:

private volatile int value;

神奇的volatile關鍵字

神奇的volatile關鍵字解決了神奇的失效數據問題。

Java變量的讀寫

Java通過幾種原子操作完成工作內存主內存的交互:

  1. lock:作用於主內存,把變量標識爲線程獨佔狀態。
  2. unlock:作用於主內存,解除獨佔狀態。
  3. read:作用主內存,把一個變量的值從主內存傳輸到線程的工作內存。
  4. load:作用於工作內存,把read操作傳過來的變量值放入工作內存的變量副本中。
  5. use:作用工作內存,把工作內存當中的一個變量值傳給執行引擎。
  6. assign:作用工作內存,把一個從執行引擎接收到的值賦值給工作內存的變量。
  7. store:作用於工作內存的變量,把工作內存的一個變量的值傳送到主內存中。
  8. write:作用於主內存的變量,把store操作傳來的變量的值放入主內存的變量中。

volatile如何保持內存可見性

volatile的特殊規則就是:

  • read、load、use動作必須連續出現
  • assign、store、write動作必須連續出現

所以,使用volatile變量能夠保證:

  • 每次讀取前必須先從主內存刷新最新的值。
  • 每次寫入後必須立即同步回主內存當中。

也就是說,volatile關鍵字修飾的變量看到的隨時是自己的最新值。線程1中對變量v的最新修改,對線程2是可見的。

volatile爲什麼沒有原子性

例如你讓一個volatile的integer自增(i++),其實要分成3步:1)讀取volatile變量值到local; 2)增加變量的值;3)把local的值寫回,讓其它的線程可見。這期間主內存中的值可能被其他修改,再將值覆蓋會導致其失效。AtomicXXX具原子性是使用了CAS(比較並交換)。

原文:https://www.cnblogs.com/monkeysayhi/p/7654460.html

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