咱們都知道volatile能保證可見性,不能保證原子性,好比i++操做java
也知道Happen-Before原則,那麼是如何確保Happen-Before原則不被指令重排序影響呢?(若是對上述描述有困惑請移步[高併發Java 三] Java內存模型和線程安全)緩存
例如你讓一個volatile的integer自增(i++),其實要分紅3步:1)讀取volatile變量值到local; 2)增長變量的值;3)把local的值寫回,讓其它的線程可見。這3步的jvm指令爲:安全
mov 0xc(%r10),%r8d ; Load inc %r8d ; Increment mov %r8d,0xc(%r10) ; Store lock addl $0x0,(%rsp) ; StoreLoad Barrier
StoreLoad Barrier就是內存屏障併發
內存屏障(memory barrier)是一個CPU指令。基本上,它是這樣一條指令: a) 確保一些特定操做執行的順序; b) 影響一些數據的可見性(多是某些指令執行後的結果)。編譯器和CPU能夠在保證輸出結果同樣的狀況下對指令重排序,使性能獲得優化。插入一個內存屏障,至關於告訴CPU和編譯器先於這個命令的必須先執行,後於這個命令的必須後執行。內存屏障另外一個做用是強制更新一次不一樣CPU的緩存。例如,一個寫屏障會把這個屏障前寫入的數據刷新到緩存,這樣任何試圖讀取該數據的線程將獲得最新值,而不用考慮究竟是被哪一個cpu核心或者哪顆CPU執行的。app
內存屏障和volatile什麼關係?上面的虛擬機指令裏面有提到,若是你的字段是volatile,Java內存模型將在寫操做後插入一個寫屏障指令,在讀操做前插入一個讀屏障指令。這意味着若是你對一個volatile字段進行寫操做,你必須知道:一、一旦你完成寫入,任何訪問這個字段的線程將會獲得最新的值。二、在你寫入前,會保證全部以前發生的事已經發生,而且任何更新過的數據值也是可見的,由於內存屏障會把以前的寫入值都刷新到緩存。 jvm
明白了內存屏障這個CPU指令,回到前面的JVM指令:從Load到store到內存屏障,一共4步,其中最後一步jvm讓這個最新的變量的值在全部線程可見,也就是最後一步讓全部的CPU內核都得到了最新的值,但中間的幾步(從Load到Store)是不安全的,中間若是其餘的CPU修改了值將會丟失。高併發
因此volatile不能保證i++操做的原子性性能