Java併發-happens-before,重排序

happens-before規則

程序順序規則:一個線程中的每個操作,happens-before於該線程中的任一後續操作
監視器鎖規則:對一個鎖的解鎖,happens-before於隨後對這個鎖的加鎖
volatile變量規則:對一個volatile域的寫,happens-before於任一後續對這個volatile域的讀
傳遞性:如果A happens-before B,且B happens-before C,那麼A happens-before C
注意:兩個操作之間具有happens-before關係,並不意味着前一個操作必須要在後一個操作之前執行!,happens-before僅僅要求前一個操作(執行的結果)對後一個操作可見,且前一個操作按順序排在第二個操作之前。

重排序

編譯器和處理器爲了優化程序性能而對指令序列進行重新排序的一種手段

數據依賴性

如果兩個操作訪問同一個變量,且這兩個操作中有一個爲寫操作,此時這兩個操作之間就存在數據依賴性。
這裏寫圖片描述

只要重排序兩個操作的執行順序,程序的執行結果就會被改變
數據依賴性僅針對單個處理器中中性的指令序列和單個線程中的執行的操作,不同處理器之間和不同線程之前的數據依賴性不被編譯器和處理器考慮

as-if-serial

不管怎麼重排序(編譯器和處理器爲了提高並行度)(單線程)程序的執行結果不能被改變。編譯器,runtime和處理器都必須遵守as-if-serial語義。
如果操作之間不存在數據依賴關係,這些操作就可能被編譯器和處理器重排序。

重排序對多線程的影響

這裏寫圖片描述
flag變量是個標記,用來標記變量a是否已被寫入。這裏假設有2個線程A,B。A首先執行write()方法,隨後B線程接着執行reader()方法。線程B在執行操作4時,能否看到線程A在操作1對共享變量a的寫入?
答案不一定能看到

由於操作1和操作2沒有數據依賴關係,編譯器和處理器可以對這兩個操作重排序;同樣操作3和操作4沒有數據依賴關係,編譯器和處理器頁可以對這兩個操作重排序。
先看下操作1和操作2重排序時,可能產生什麼效果
這裏寫圖片描述
虛箭線標識錯誤的讀操作
操作1和操作2做了重排序。程序執行時,線程A首先寫標記變量flag,隨後線程B讀這個變量。由於條件判斷爲真,線程B將讀取變量a。此時,變量a還沒有被線程A寫入

操作3和操作4重排序時
這裏寫圖片描述

當代碼中存在控制依賴性時,會影響指令序列執行的並行度。爲此,編譯器和處理器會採用猜測來執行客服控制相關性對並行度的影響。以處理器的猜測執行爲例,執行線程B的處理器可以提前讀取並計算a*a,然後把計算結果臨時保存到一個名爲重排序緩衝的硬件緩存中
,當操作3的條件爲真時,就把該計算結果寫入變量i中。
在單線程中,對存在控制以來的操作重排序,不會改變執行結果,但在多線程中,對存在控制依賴的操作作重排序,可能會改變程序的執行結果。

發佈了125 篇原創文章 · 獲贊 36 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章