編譯器和處理器必須同時遵守重排規則。多核處理器需使用記憶體屏障指令來確定一緻性。即使編譯器優化掉了一個字段通路(因為一個讀入的值未被使用),需要産生記憶體屏障,就像這個通路仍然需要保護。(可參考下面的優化掉記憶體屏障的章節)。
記憶體屏障指令僅直接控制CPU與其緩存之間,與垃圾回收機制中“寫屏障(write barriers)”無關。
一、重排序
編譯器或者CPU的代碼的結構重排排序,達到最佳效果。
(1)編譯器重排
CPU隻讀一次的x和y值。不需反複讀取寄存器來交替x和y值。
(2)處理器重排
寫緩存區沒有及時重新整理,使得處理器執行的讀寫操作與記憶體上順序不一緻。
處理器A讀b=0,處理器B讀a=0。A1寫a=1先寫到處理器A的寫緩存區中,此時記憶體中a=0。如果這時處理器B從記憶體中讀a,讀到的将是0。
可能會出現x,y都是0。
二、記憶體屏障
為了解決上述問題,處理器提供記憶體屏障指令(Memory Barrier):
寫記憶體屏障(Store Memory Barrier):處理器将存儲緩存值寫回主存(阻塞方式)。
讀記憶體屏障(Load Memory Barrier):處理器,處理失效隊列(阻塞方式)。
保證兩個操作之間資料的可見性。
volatile讀前插讀屏障,寫後加寫屏障,避免CPU重排導緻的問題,實作多線程之間資料的可見性。
三、記憶體屏障的種類
StoreLoad開銷最大。萬能屏障,兼具其它三種記憶體屏障功能。執行時,處理器通常要把寫緩沖區中的資料全部重新整理的記憶體中
對于處理器來說,記憶體屏障會導緻cpu緩存的重新整理,重新整理時,會遵循緩存一緻性協定。
lock:
解鎖時,jvm會強制重新整理cpu緩存,導緻目前線程更改,對其他線程可見。
volatile:
(1)對于寫操作:對變量更改完之後,要立刻寫回到主存中。
(2)對于讀操作:對變量讀取的時候,要從主存中讀,而不是緩存。
final:即時編譯器在final寫操作後,會插入記憶體屏障,來禁止重排序,保證可見性