1. 程式人生 > 實用技巧 >無鎖、自旋鎖、偏向鎖、輕量級鎖和重量級鎖

無鎖、自旋鎖、偏向鎖、輕量級鎖和重量級鎖

無鎖

無鎖是指執行緒通過無限迴圈來執行更新操作,如果執行成功就退出迴圈,如果執行失敗(有其他執行緒更新了值),則繼續執行,直到成功為止。CAS操作就屬於無鎖。如果從效能的角度來看,無鎖狀態的效能是非常高的。

自旋鎖

自旋鎖是一種通過讓執行緒不釋放當前的CPU執行一個忙迴圈,來嘗試獲取鎖的方式。自旋鎖的前提假設是鎖被其它執行緒佔用的時間很短。如果其它執行緒佔用鎖的時間很長,那麼自旋的執行緒只會白白消耗處理器資源,而不會做任何有用的工作,反而帶來效能上的浪費。自旋次數的預設值是10次,使用者可以通過使用引數-XX:PreBlockSpin來更改。

HotSpot虛擬機器物件頭Mark Word

儲存內容 標誌位 狀態
物件雜湊碼、物件分代年齡 01 未鎖定
指向鎖記錄的指標 00 輕量級鎖定
偏向執行緒ID、偏向時間戳、物件分代年齡 01 可偏向
指向重量級鎖的指標 10 膨脹(重量級鎖定)
空,不需要記錄資訊 11 GC標記

偏向鎖

當一個執行緒獲取了鎖,如果在接下來的執行過程中,該鎖沒有被其它的執行緒獲取,則持有偏向鎖的執行緒將永遠不需要再進行同步。當有另外一個執行緒區嘗試獲取這個鎖的時候,偏向模式就宣告結束。偏向鎖的前提假設是當一個執行緒獲取鎖,後面還有大概率該執行緒還會需要繼續持有這把鎖

虛擬機器啟用偏向鎖的引數-XX:UseBiasedLocking。如果當前偏向鎖已啟動,當鎖物件第一次被執行緒獲取的時候,虛擬機器將會把物件頭中的標誌位設為01

,即偏向模式。同時使用CAS操作把獲取到這個鎖的執行緒的ID記錄在物件的Mark Word之中,如果CAS操作成功,持有偏向鎖的執行緒以後每次進入這個鎖的同步塊時,虛擬機器都可以不用再進行同步操作了。

輕量級鎖

在程式碼進入同步塊的時候,如果此同步物件沒有被鎖定(鎖標誌位為01狀態),虛擬機器首先將在當前執行緒的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用於儲存鎖物件目前的Mark Word的拷貝。

然後,虛擬機器將使用CAS操作嘗試將物件的Mark Word更新為指向Lock Record的指標。如果這個更新動作成功了,那麼這個執行緒就擁有了該物件的鎖,並且物件Mark Word的鎖標誌位(Mark Word的最後2bit)將轉變為00

,即表示此物件處於輕量級鎖定狀態。輕量級鎖的前提假設是對於絕大部分的鎖,在整個同步週期內都是不存在競爭的,通過CAS操作來避免時候互斥鎖的開銷

重量級鎖

當有兩個及以上的執行緒爭用同一個鎖,那麼輕量級鎖就不再有效,要膨脹為重量級鎖。鎖標誌的狀態值變為10,Mark Word中儲存的就是指向重量級鎖(互斥量)的指標,後面等待鎖的執行緒也要進入阻塞狀態。

在討論鎖之間的轉換狀態時,首先需要理解以下幾個問題:

  • 偏向鎖只有設定了-XX:UseBiasedLocking引數才會存在
  • 假設啟用了偏向鎖,物件頭的鎖標誌位是01(和未鎖定狀態一樣),但是儲存的內容是偏向執行緒ID、偏向時間戳
  • 當執行緒獲取偏向鎖是通過CAS操作將物件頭中儲存的偏向執行緒ID更新為當前執行緒的ID
  • 物件是否被鎖定是指物件頭是否指向執行緒的鎖記錄(Lock Record)
  • 只有是輕量級鎖或者重量級鎖時物件才會被鎖定

鎖之間狀態轉換

結合自己的理解繪製了一個鎖之間狀態轉化的關係圖: