1. 程式人生 > >初識Java虛擬機器(11)記憶體模型

初識Java虛擬機器(11)記憶體模型

    Java記憶體模型的主要目標是定義程式中各個變數的訪問規則,即在虛擬機器中將變數儲存到記憶體和從記憶體中取出變數這樣的實現細節。它包括了例項欄位、靜態欄位和構成陣列物件的元素,但不包括區域性變數與方法引數,因為後者是執行緒私有的,不會被共享,也不會存在競爭問題。     Java記憶體模型規定了所有的變數都儲存在主記憶體中。每條執行緒還有自己的工作記憶體,執行緒的工作記憶體中儲存了被該執行緒使用到的變數的主記憶體副本拷貝,執行緒對變數的所有操作都必須在工作記憶體中進行,而不能直接讀寫主記憶體的變數。不同執行緒直接也無法直接訪問對方工作記憶體中的變數,執行緒間變數值傳遞均需要通過主記憶體完成。
    記憶體間互動操作 Java記憶體模型中定義了以下8中操作來完成一個變數如何從主記憶體拷貝到工作記憶體、如何從工作記憶體同步回主記憶體之類的實現細節,虛擬機器實現時必須保證下面的每一種操作都是原子的、不可再分的:     (1)lock:作用於主記憶體,把一個變數標識為一條執行緒獨佔。     (2)unlock:作用於主記憶體,把一個處於lock狀態的變數釋放出來。     (3)read:作用於主記憶體,把一個變數的值從主記憶體傳輸到執行緒的工作記憶體中,以便load動作使用。     (4)load:作用於工作記憶體,把從read操作中得到的變數放入工作記憶體的變數副本中。     (5)use:作用於工作記憶體,把工作記憶體中的一個變數的值傳遞給執行引擎,每當虛擬機器遇到一個需要使用變數值的位元組碼指令時將會執行這個操作。
    (6)assign:作用於工作記憶體,將從執行引擎中收到的變數值賦值給記憶體中的變數,每當虛擬機器遇到一個給變數賦值的位元組碼指令時執行該操作。     (7)store:作用於工作記憶體的變數,它把工作記憶體中的一個變數值傳送給主記憶體中。     (8)write:作用於主記憶體,把從store操作得到的變數值放到主記憶體中。     主記憶體到工作記憶體直接的同步資料需要兩步,一是傳輸,而是改寫。     上述八個操作必須滿足如下規則: (1)read和load、store和write必須成對出現,不允許主記憶體或工作記憶體不接收的情況發生。     (2)一旦發生assign,工作記憶體必須和主記憶體進行同步。
    (3)若未發生assign,工作記憶體不允許與主記憶體同步。     (4)一個新的變數只能在主記憶體中“誕生”,不允許在工作記憶體中直接使用一個未被load或assign的變數。     (5)一個變數在同一時刻只允許一條執行緒對其進行lock操作,但lock操作可被同一條執行緒重複多次,只有執行相同次數的unlock,變數才會被解鎖。     (7)如果對一個變數執行lock操作,那將會清空工作記憶體中此變數的值,在執行引擎使用這個變數前,需要重新執行load或assign操作初始化變數的值。     (8)不允許unlock一個未lock的變數,不允許unlock一個被其他執行緒lock的變數。     (9)對一個變數lock前,必須先同步回主記憶體(stroe、write)。     對於volatile的特殊規則 (1)保證此變數對所有執行緒的可見性,當一條執行緒修改了這個變數的值後,新值對於其他變數來說是立即可知的。而對於普通變數,只有在執行緒A回寫了之後執行緒B讀取,新變數值才對B可見。     注意:(1)並不能保證基於volatile變數的運算在併發下是安全的,因為它只保證了在工作記憶體中的一致性,一旦發生了use和assign,從工作記憶體到運算元棧時,虛擬機器棧中的操作不能保證一致性。例如併發對i進行運算時,每個執行緒在將i傳到操作棧時可以保證I的正確性,但操作棧是併發進行的,這時兩個操作棧同時對i進行操作,不能保證I的順序執行了。     只有1、當運算結果不依賴變數的當前值,或者能確保同時只有單一執行緒修改變數值。2、變數不需要與其他的狀態變數共同參與不變約束時才能使用volatile關鍵字。 (2)禁止指令重排序優化,普通變數只保證該方法執行過程中所有依賴賦值結果的地方都能取到正確的結果,而不能保證變數賦值操作的順序與程式程式碼中的執行順序一致