1. 程式人生 > >JMM主記憶體與工作記憶體互動

JMM主記憶體與工作記憶體互動

Java虛擬機器記憶體模型中定義了8種關於主記憶體和工作記憶體的互動協議操作:

  • lock:作用於主記憶體的變數,把一個變數標識為一條執行緒獨佔狀態。
  • unlock:作用於主記憶體的變數,把一個處於鎖定狀態的變數釋放出來,釋放後的變數可以被其他執行緒鎖定。
  • read:作用於主內的變數,把一個變數的值從主記憶體傳輸到執行緒的工作記憶體中,以便隨後的load動作使用。
  • load:作用於工作記憶體的變數,把read讀取操作從主記憶體中得到的變數值放入工作記憶體的變數拷貝中。
  • use:作用於工作記憶體的變數,把工作記憶體中一個變數的值傳遞給java虛擬機器執行引擎,每當虛擬機器遇到一個需要使用到變數值的位元組碼指令時將會執行該操作。
  • assign:作用於工作記憶體變數,把一個從執行引擎接收到的變數的值賦值給工作變數,每當虛擬機器遇到一個給變數賦值的位元組碼時將會執行該操作。
  • store:作用於工作記憶體的變數,把工作記憶體中一個變數的值傳送到主記憶體中,以便隨後的write操作使用。
  • write:作用於主記憶體的變數,把store操作從工作記憶體中得到的變數值放入主記憶體的變數中。

Java記憶體模型對上述8種操作有如下的約束:

  • 把一個變數從主記憶體複製到工作記憶體中必須順序執行read讀入操作和load載入操作。把一個變數從工作記憶體同步回主記憶體中必須順序執行store儲存操作和write寫入操作。
    read和load操作之間、store和write操作之間可以插入其他指令,但是read和load操作、store和write操作必須要按順序執行,即不允許read和load、store和write操作之一單獨出現。
  • 不允許一個執行緒丟棄它的最近的assign賦值操作,即工作記憶體變數值改變之後必須同步回主記憶體。只有發生過assign賦值操作的變數才需要從工作記憶體同步回主記憶體。
  • 一個新變數只能在主記憶體中產生,不允許在工作記憶體中直接使用一個未被初始化(load或assign)的變數,即一個變數在進行use和store操作之前,必須先執行過assgin和load操作。
  • 一個變數在同一時刻只允許一條執行緒對其進行lock鎖定操作,但是lock鎖定可以被一條執行緒重複執行多次,多次執行lock之後,只有執行相同次數的unlock操作變數才會被解鎖。
  • 如果對一個變數執行lock鎖定操作,將會清空工作記憶體中該變數的值,在執行引擎使用這個變數前,需要重新執行load或assign操作初始化變數的值。
  • 如果一個變數事先沒有被lock鎖定,則不允許對這個變數進行unlock解鎖操作,也不允許對一個被別的執行緒鎖定的變數進行unlock解鎖。
  • 一個變數進行unlock解鎖操作之前,必須先把此變數同步回主記憶體中(執行store和write操作)。

當一個變數被宣告為volatile之後,JMM對其做了特殊規則:

  1. volatile變數的操作必須嚴格按load->use順序,前一個動作是load時才能執行use動作,後一個動作是use時才能執行load動作,即每次在工作記憶體中使用變數前必須先從主記憶體中重新整理最新的值,以保證能看到其他執行緒對變數的最新修改。
  2. volatile變數的操作必須嚴格按assign->store順序,前一個動作是assign時才能執行store動作,後一個動作是store時才能執行assign動作,即每次在工作記憶體為變數賦值之後必須將變數的值同步回主記憶體,以保證讓其他執行緒能看到變數的最新修改。
  3. 若執行緒對volatile變數V的assign或者use操作先於對volatile變數W的assign或者use操作,則執行緒對volatile變數A的read/load或者store/write操作也必定先於對volatile變數B的read/load或者store/write操作。

volatile變數讀操作的效能消耗與普通變數幾乎沒什麼差別,但是寫操作則可能慢一些,因為它需要在原生代碼中插入許多記憶體屏障指令來保證處理器不發生指令重排。