volatile,可見性,有序性

volatile,可見性,有序性

volatile的特性

  • 可見性:對一個volatile變量的讀,總能獲取其餘任意線程對該變量最後的寫入。
  • 有序性:JMM會限制volatile變量相關的編譯器重排序和處理器重排序。

內存語義的的實現

1.可見性的實現基於volatile的讀取,寫入兩個操做的內存語義。
  • volatile寫的內存語義:當寫入一個volatile變量的時候,JMM將線程工做內存中的該變量的值刷新到主內存。
  • volatile讀的內存語義:當讀取一個volatile變量的時候,JMM首先將該線程工做內存中的這個變量設置爲無效,迫使該線程從新從主內存獲取最新的有效值。
  • 二者結合起來,就實現了,volatile變量的可見性,由於一個線程去讀取volatile變量的時候獲取的確定是最新的值。
2.有序性的實現基於JMM針對編譯器制定的volatile重排序表
可否重排序 - 第二個操做 -
第一個操做 普通讀/寫 volatile讀 volatile寫
普通讀/寫 NO
volatile讀 NO NO NO
volatile寫 NO NO
3.最核心的部分仍是內存屏障的使用,內存屏障實現了volatile讀寫的內存語義,也實現了重排序表

理解JMM如何實現volatile的兩層內存語義的關鍵是==內存屏障==。volatile的兩層內存語義都是使用內存屏障來實現的。緩存

首先,對4中內存屏障的介紹: 內存屏障用於控制特定條件下的重排序和內存可見性問題。
  • LoadLoad屏障:對於這樣的語句Load1; LoadLoad; Load2,在Load2及後續讀取操做要讀取的數據被訪問前,保證Load1要讀取的數據被讀取完畢。
  • StoreStore屏障:對於這樣的語句Store1; StoreStore; Store2,在Store2及後續寫入操做執行前,保證Store1的寫入操做對其它處理器可見。
  • LoadStore屏障:對於這樣的語句Load1; LoadStore; Store2,在Store2及後續寫入操做被刷出前,保證Load1要讀取的數據被讀取完畢。
  • StoreLoad屏障:對於這樣的語句Store1; StoreLoad; Load2,在Load2及後續全部讀取操做執行前,保證Store1的寫入對全部處理器可見。它的開銷是四種屏障中最大的。 在大多數處理器的實現中,這個屏障是個萬能屏障,兼具其它三種內存屏障的功能。
再看看JMM內存屏障的插入策略
  • volatile寫操做前面插入一個StoreStore屏障

對比StoreStore屏障的定義,這裏的volatile寫是那個Store2,該屏障保證在volatile寫執行以前,Store1的寫入操做對其餘處理器可見,那麼能夠得出,該屏障不只保證了Store1已經執行完畢(有序性),也保證了可見性。線程

  • 每一個volatile寫操做的後面插入一個StoreLoad屏障

對比StoreLoad屏障的定義,這裏的volatile寫是那個Store1,該屏障也保證了有序性和可見性。其餘都是相似的。code

  • 每一個volatile讀操做後面插入一個LoadLoad屏障。
  • 每一個volatile讀操做後面插入一個LoadStore屏障。
4.從CPU的角度,看看可見性如何實現
//假設有個volatile變量instance,對其進行寫操做
instance = new Singleton();

在X86處理器下看看其對應的彙編代碼的一部分:排序

lock add1 $0*0

lock前綴的指令在多核處理器下引起了兩件事:內存

  • 當前處理器緩存行的數據寫回系統內存
  • 使其餘處理器緩存行中緩存的該內存地址的數據無效
相關文章
相關標籤/搜索