Java內存模型

緩存一致性

在多處理器系統中,每個處理器都有自己的高速緩存,而它們又共享同一塊內存。當多個處理器的運算任務都涉及到同一塊內存區域時,將可能導致各自的緩存數據不一致。爲了解決一致性的問題,需要各個處理器訪問緩存是都遵守一些協議。
在這裏插入圖片描述

Java內存模型

通過Java內存模型來屏蔽掉各種硬件和操作系統的內存訪問差異,已實現讓Java程序在各個平臺下都能達到一致的內存訪問效果。
主要目標是定義程序中各個變量的訪問規則,即在虛擬機中將變量存儲到內存和從內存中取出變量這樣的底層細節。此處的變量(Variables)與java編程中所說的變量有所區別,它包括實例字段靜態字段構成數組對象的元素,但不包括局部變量與方法參數,因爲後者是線程私有的,不會被共享,自然不存在競爭問題。
主內存(虛擬機內存的一部分):所有的變量(實例字段、靜態字段、構成數組對象的元素)
工作內存(虛擬機棧的一部分):線程不能直接讀寫主內存中的變量,需要拷貝主內存的副本進行操作,線程之間無法互相共享工作內存中的數據,需要通過主內存進行通信
在這裏插入圖片描述

內存間交互操作

  • lock(鎖定):作用於主內存變量,把一個變量標識爲一條線程獨佔狀態
  • unlock(解鎖):作用於主內存變量,把一個鎖定的變量釋放出來,只有被釋放的變量才能被其它引用使用
  • read:作用於主內存,把一個變量的值從主內存傳遞到工作內存
  • load:作用於工作內存,將read讀取的變量值放入工作內存的副本中
  • use:作用於工作內存,遇到執行執行,將工作內存中的副本值傳遞到執行引擎
  • assign:作用於工作內存,將執行引擎中的值賦值給工作內存中的副本
  • store:作用於工作內存,將工作內存中的副本值傳遞到主內存
  • write:作用於主內存,將store傳遞的副本值放入對應的主內存變量中

在這裏插入圖片描述
內存操作八條規則

  1. 不允許read、load、store和write操作之一單獨出現,即由讀操作開始,必須有寫操作結束
  2. 不允許丟棄assign操作,副本在工作內存中改變之後必須被寫完主內存
  3. 新變量只能在內存中誕生,不允許在工作內存中直接使用一個未被初始化的變量(load、assign),即對一個變量實施操作之前,必須先執行過assign和load操作
  4. 一個變量僅允許一條線程對其進行lock操作,但是擁有該變量鎖的線程可以對該變量進行多次lock操作,調用多次lock操作時,只有調用了相同的unLock操作,對象纔有可能被其它線程使用
  5. 如果一個變量被執行lock操作,那將會清空工作內存中此變量的值,在執行引擎使用這個變量之前需要重新執行read和load操作
  6. 若果一個變量事先沒有被lock鎖定,不允許對變量執行unlock操作,也不允許區unlock一個被其它線程鎖定的對象
  7. 一個變量執行unlock之前,必須先把此變量同步回主內存中
  8. 不允許一個線程無原因的(沒有發生過任何assign操作)把數據從線程的工作內存同步回主內存

Volatile型變量的特殊規整

**可見性保證:**工作內存中,每次對volatile變量使用之前都會進行刷新,修改之後也會馬上對內存進行修改
**禁止指令重排序優化:**在經過編譯之後,無前後依賴關係的操作指令可能會出現執行順序與源程序中定義順序不一致的情況(可將無依賴關係的計算任務交由不同電路結構處理);實現機制,在volatile變量使用處插入一個屏障,使得屏障之後的操作指令無法在屏障之前的操作指令執行之前執行
**原子性:**volatile變量無法保證執行引擎中的一致性,因爲執行引擎中可能存在非原子性的操作,導致執行引擎中的操作數不是最新的情況出現

Volatile消除原子性操作適用條件:

  • 運行結果並不依賴變量的當前值,或者能夠確保只有單一線程修改變量的值
  • 變量不需要與其它狀態變量共同參與不變約束

內存屏障適用範圍:

  • 只有一個CPU訪問內存時,並不需要內存屏障;
  • 但如果有兩個或者多個CPU訪問同一塊內存時,且其中有一個在觀測另一個,就需要內存屏障來保證一致性

double、long型變量的特殊規則

Java內存模型要求lock、unlock、read、load、assign、use、store、write這8個基本操作都具有原子性
long、double非原子性協定:允許虛擬機將沒有聲明爲volatile的long、double的64位數據讀寫操作分成兩次32位數據的讀寫操作
非原子協定問題:多線程情況下,可能出現寫一半的同時,另一條線程開始讀的情況
實際情況:目前的商用虛擬機都對64位數據的兩次32位讀寫操作實現了原子性操作

原子性、可見性與有序性

  • 原子性:Java內存模型直接保證原子性的操作包括基本類型的8中基本操作(除非原子協定之外),同時synchronized之間也具有原子性
  • 可見性:至當前線程修改了變量的值,其它線程可以立刻知道(volatile、final、synchronized可實現可見性)。
    實現方式:每次使用變量之前,對變量進行一次刷新,每次對變量修改之後保證立刻寫會內存。
  • 有序性:本線程上,所有的操作都是有序的;但是在一個線程中觀察另一個線程,所有的操作都是無序的。volatile與synchronized可以實現有序性。
    volatile通過禁止指令重排實現,synchronized則是由“一個變量在同一時刻只允許一條線程對其進行lock操作”,這條規則決定了持有同一個鎖的兩個同步塊只能串行地進入。

先行發生原則

  • 程序次序規則:一個線程內,按照程序代碼順序(控制流順序執行)
  • 管程鎖定規則:unlock操作先行於後面對同一個鎖的lock操作
  • volatile變量規則:volatile變量的寫操作優先於讀操作
  • 線程啓動規則:Thread的start方法先行於此線程的每一個動作
  • 線程終止規則:線程中所有操作都優先於終止操作。通過Thread.join()方法結束、Thread.isAlive()的返回值等手段檢測到線程已經終止
  • 線程中斷規則:對線程interupt()方法的調用先行發生於被中斷線程的代碼檢測到中斷事件的發生,可以通過Thread.interrupted()方法檢測到是否有中斷髮生
  • 對象終結規則:一個對象初始化完成(構造函數執行結束)先行於finalize()方法開始
  • 傳遞性:A先行於B,B先行於C,可得出A先行於C
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章