volatile變數自身具有下列兩點特性:
可見性:鎖的happens-before規則保證釋放鎖和獲取鎖的兩個執行緒之間的記憶體可見性。意味著對一個volatile變數的讀,總是能看到(任意執行緒)對這個volatile變數最後的寫入。
原子性:對任意單個volatile變數的讀/寫具有原子性,但類似於volatile++這種複合操作不具有原子性。
從記憶體語義的角度來說,volatile與鎖有相同的效果:volatile寫和鎖的釋放有相同的記憶體語義;volatile讀與—鎖的獲取—有相同的記憶體語義。
volatile 寫-讀的記憶體語義
volatile 寫的記憶體語義:當寫一個volatile變數的時候,JMM會把該執行緒對應的本地記憶體中的共享變數的值重新整理到主記憶體中
volatile 讀的記憶體語義:當讀一個volatile變數時,JMM會把該執行緒對應的本地記憶體置為無效。執行緒接下來將從主記憶體中讀取共享變數
下面對volatile寫和volatile讀的記憶體語義做個總結:
- 執行緒A寫一個volatile變數,實質上是執行緒A向接下來將要讀這個volatile變數的某個執行緒發出了(其對共享變數所在修改的)訊息。
- 執行緒B讀一個volatile變數,實質上是執行緒B接收了之前某個執行緒發出的(在寫這個volatile變數之前對共享變數所做修改的)訊息。
- 執行緒A寫一個volatile變數,隨後執行緒B讀這個volatile變數,這個過程實質上是執行緒A通過主記憶體向執行緒B傳送訊息。
volatile重排序規則
- 當第二個操作是volatile寫時,不管第一個操作是什麼,都不能重排序。這個規則確保volatile寫之前的操作不會被編譯器重排序到volatile寫之後。
- 當第一個操作是volatile讀時,不管第二個操作是什麼,都不能重排序。這個規則確保volatile讀之後的操作不會被編譯器重排序到volatile讀之前。
- 當第一個操作是volatile寫,第二個操作是volatile讀或者是volatile寫時,不能重排序
為了實現volatile的記憶體語義,編譯器在生成位元組碼時,會在指令序列中插入記憶體屏障來禁止特定型別的處理器重排序。
下面是基於保守策略的JMM記憶體屏障插入策略(這些Store和Loade含義請看我另外一篇部落格)http://www.cnblogs.com/prayers/p/7478668.html:
- 在每個volatile寫操作的前面插入一個StoreStore屏障。
- 在每個volatile寫操作的後面插入一個StoreLoad屏障。
- 在每個volatile讀操作的後面插入一個LoadLoad屏障。
- 在每個volatile讀操作的後面插入一個LoadStore屏障。
從編譯器重排序規則和處理器記憶體屏障插入策略來看,只要volatile變數與普通變數之間的重排序可能會破壞volatile的記憶體語意,這種重排序就會被編譯器重排序規則和處理器記憶體屏障插入策略禁止。