天天看點

JAVA偏實施_JVM内部細節之二:偏向鎖(Biased Locking)

在前面一片文章《JVM内部細節之一:synchronized關鍵字及實作細節》中已經提到過偏向鎖的概念,在了解什麼是偏向鎖前必須先了解什麼是輕量級鎖(Lightweight Locking)。引入偏向鎖是為了在無多線程競争的情況下盡量減少不必要的輕量級鎖執行路徑,因為輕量級鎖的擷取及釋放依賴多次CAS原子指令,而偏向鎖隻需要在置換ThreadID的時候依賴一次CAS原子指令(由于一旦出現多線程競争的情況就必須撤銷偏向鎖,是以偏向鎖的撤銷操作的性能損耗必須小于節省下來的CAS原子指令的性能消耗)。下面看具體細節:

一、對象頭中的Mark Word布局

JAVA偏實施_JVM内部細節之二:偏向鎖(Biased Locking)

在上一篇文章中所讨論的輕量級鎖中在我參考的Paper中對于重量級鎖的實作并沒有通過狀态位來表現而是直接通過在輕量級鎖的Monitor Record中關聯一個底層作業系統的互斥信号量來實作重量級鎖的操作(并不影響我們了解JVM内部鎖的運作過程),在偏向鎖的處理過程中并不涉及重量級鎖,我們這裡隻需要關心biasable和lightweight locked兩種狀态。在JDK1.6以後預設已經開啟了偏向鎖這個優化,我們可以通過在啟動JVM的時候加上-XX:-UseBiasedLocking參數來禁用偏向鎖(在存在大量鎖對象的建立并高度并發的環境下禁用偏向鎖能夠帶來一定的性能優化)。

二、偏向鎖的擷取過程(假設開啟了偏向鎖優化):

(1)初始時對象處于biasable狀态,并且ThreadID為0即biasable & unbiased狀态(這裡不讨論epoch和age)

(2)當一個線程試圖鎖住一個處于biasable & unbiased狀态的對象時,通過一個CAS将自己的ThreadID放置到Mark Word中相應的位置,如果CAS操作成功進入第(3)步否則進入(4)步

(3)當進入到這一步時代表目前沒有鎖競争,Object繼續保持biasable狀态,但是這時ThreadID字段被設定成了偏向鎖所有者的ID,然後進入到第(6)步

(4)目前線程執行CAS擷取偏向鎖失敗(這一步是偏向鎖的關鍵),表示在該鎖對象上存在競争并且這個時候另外一個線程獲得偏向鎖所有權。當到達全局安全點(safepoint)時獲得偏向鎖的線程被挂起,并從偏向鎖所有者的私有Monitor Record清單中擷取一個空閑的記錄,并将Object設定為LightWeight Lock狀态并且Mark Word中的LockRecord指向剛才持有偏向鎖線程的Monitor record,最後被阻塞在安全點的線程被釋放,進入到輕量級鎖的執行路徑中,同時被撤銷偏向鎖的線程繼續往下執行同步代碼。

(5)當一個線程試圖鎖住一個處于biasable & biased并且ThreadID不等于自己的ID時,這時由于存在鎖競争必須進入到第(4)步來撤銷偏向鎖。

(6)運作同步代碼塊

二、偏向鎖的解鎖過程:

(1)偏向鎖解鎖過程很簡單,隻需要測試下是否Object上的偏向鎖模式是否還存在,如果存在則解鎖成功不需要任何其他額外的操作。

三、參考資料: