Java鎖升級的實現過程
物件記憶體佈局
Java物件在記憶體中儲存的佈局可以分為3塊區域: 物件頭、例項資料、對齊填充。
物件頭,分為兩個部分,第一個部分儲存物件自身的執行時資料,又稱為Mark Word
,32位虛擬機器佔32bit,64位虛擬機器佔64bit。如圖所示,不同鎖狀態下,Mark Word的結構,理解下面要介紹的各種鎖,和鎖升級過程,都需要先充分了解Mark Word
的結構。
第二部分是型別指標,指向類元資料指標,虛擬機器通過此指標,確定該物件屬於那個類的例項。
輕量級鎖
輕量級鎖是相對於重量級鎖(Synchrnoized
)而言的,本意是在沒有多執行緒競爭的前提下,減少傳統的重量級鎖使用作業系統互斥量產生的效能消耗。
輕量級鎖的獲取
執行緒進入同步塊時,如果此同步物件沒有被鎖定(即鎖標誌位為01
,是否為偏向鎖為0
),虛擬機器在當前執行緒的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用於儲存鎖物件目前的一個Mark Word
的copy
然後虛擬機器使用CAS操作,嘗試將Mark World
更新為指向Lock Record
的指標,如果更新成功,那麼執行緒擁有了該物件的鎖,並且將鎖標誌位置位00
,如圖所示
一旦有兩條以上的執行緒搶佔該鎖,輕量級鎖會升級為重量級鎖。鎖標誌位置為10
,Mark Word儲存的就是指向重量級鎖的指標
輕量級鎖釋
- 放如果物件的Mark Word仍然指向著執行緒的鎖記錄, 那就用CAS操作把物件當前的Mark Word和執行緒中複製的Displaced Mark Word替換回來, 如果替換成功, 整個同步過程就完成了。
- 如果替換失敗, 說明有其他執行緒嘗試過獲取該鎖,輕量級鎖膨脹為重量級鎖,那就要在釋放鎖的同時, 喚醒被掛起的執行緒。
偏向鎖
引入偏向鎖的目的是在沒有多執行緒競爭的前提下,進一步減少執行緒同步的效能消耗。
偏向鎖的獲取
開啟偏向鎖模式後,鎖第一次被執行緒獲取的時候,虛擬機器會把物件頭中是否為偏向鎖
的標誌位設位0
,同時使用CAS操作把獲取到這個鎖的執行緒的ID記錄在物件的Mark Word之中。
當有另外一個執行緒去嘗試獲取這個鎖時, 偏向模式就宣告結束。 根據鎖物件目前是否處於被鎖定的狀態,撤銷偏向( Revoke Bias) 後恢復到未鎖定( 標誌位為“01”)或輕量級鎖定( 標誌位為“00”) 的狀態
偏向鎖的釋放
偏向鎖,並沒有顯式的鎖釋放過程,主要依靠鎖的批量再偏向(Bulk Rebias)機制實現鎖釋放。
該機制的主要工作原理如下:
- 引入一個概念 epoch,其本質是一個時間戳 , 代表了偏向鎖的有效性,從前文描述的物件頭結構中可以看到, epoch 儲存在可偏向物件的 MarkWord 中。
- 除了物件中的 epoch,物件所屬的類 class 資訊中, 也會儲存一個 epoch 值,每當遇到一個全域性安全點時, 如果要對 class 進行批量再偏向, 則首先對 class 中儲存的 epoch 進行增加操作, 得到一個新的 epoch_new
- 然後掃描所有持有 class 例項的執行緒棧,根據執行緒棧的資訊判斷出該執行緒是否鎖定了該物件, 僅將epoch_new 的值賦給被鎖定的物件中。
- 退出安全點後, 當有執行緒需要嘗試獲取偏向鎖時, 直接檢查 class中儲存的 epoch 值是否與目標物件中儲存的 epoch 值相等,如果不相等, 則說明該物件的偏向鎖已經無效了, 可以嘗試對此物件重新進行偏向操作。
整個鎖升級過程
參考文章
偏向鎖,輕量級鎖與重量級鎖的區別與膨脹
Java中的偏向鎖,輕量級鎖, 重量級鎖解析
到此這篇關於Java鎖升級的實現過程的文章就介紹到這了,更多相關Java鎖升級內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!