重排序:是指編譯器和處理器為了優化程式效能而對指令序列進行重新排序的一種手段
1.、編譯器優化的重排序。編譯器在不改變單執行緒程式語義的前提下,可以重新安排語句的執行順序。
2.、指令級並行的重排序。現代處理器採用了指令級並行技術來將多條指令重疊執行。如果不存在資料依賴性,處理器可以改變語句對應機器指令的執行順序。
3.、記憶體系統的重排序。由於處理器使用快取和讀/寫緩衝區,這使得載入和儲存操作看上去可能是在亂序執行。
從java原始碼到最終實際執行的指令序列,會分別經歷下面三種重排序:
上述的1屬於編譯器重排序,2和3屬於處理器重排序。這些重排序都可能會導致多執行緒程式出現記憶體可見性問題。對於編譯器,JMM的編譯器重排序規則會禁止特定型別的編譯器重排序。對於處理器重排序,
JMM的處理器重排序規則會要求java編譯器在生成指令序列時,插入特定型別的記憶體屏障指令,通過記憶體屏障指令來禁止特定型別的處理器重排序。
資料依賴:資料依賴分為三種型別
寫後讀:寫一個變數之後,在讀這個變數
寫後寫:寫一個變數之後,在寫這個變數
讀後寫:讀一個變數之後,在寫這個變數
上面三種型別,只要重排序兩個操作的執行順序,程式的執行結果就會被改變
編譯器和處理器不會改變存在資料依賴關係的兩個操作的執行順序(這裡所說的資料依賴性僅針對單個處理器中執行的指令序列和單個執行緒中執行的操作,不同處理器之間和不同執行緒之間的資料依賴性不被編
譯器和處理器考慮)
as-if-serial語義
as-if-serial語義的意思指:不管怎麼重排序(編譯器和處理器為了提高並行度),(單執行緒)程式的執行結果不能被改變。編譯器,runtime 和處理器都必須遵守as-if-serial語義。
為了遵守as-if-serial語義,編譯器和處理器不會對存在資料依賴關係的操作做重排序,因為這種重排序會改變執行結果。但是,如果操作之間不存在資料依賴關係,這些操作可能被編譯器和處理器重排序
在單執行緒程式中,對存在控制依賴的操作重排序,不會改變執行結果(這也是as-if-serial語義允許對存在控制依賴的操作做重排序的原因);但在多執行緒程式中,對存在控制依賴的操作重排序,可能會改變程式的執行結果。