Synchronized鎖的升級

Java對象頭

Synchronized用的鎖是存在java對象頭裏的。如果對象是數組,虛擬機則使用三個字寬(Word)存儲對象頭,如果是非數組類型則用2個字寬存儲,在32位虛擬機 1個字寬=4個字節

32位jvm java對象頭的存儲結構
鎖狀態 25bit 4bit 1bit 2bit
23bit 2bit   是否是偏向鎖 鎖標誌位
無鎖狀態 對象的hashCode 對象分代年齡 0 01
偏向鎖 線程ID EPOCH 對象分代年齡 1 01
輕量級鎖 指向棧中鎖記錄的指針 00
重量級鎖 指向互斥量(重量級鎖)的指針 11
GC標記 01

偏向鎖

偏向鎖,當只有一個線程訪問同步代碼塊並獲取鎖時,會在對象頭和棧幀中鎖記錄裏,存儲鎖偏向的線程ID,以後進入和退出時,不需要進行CAS操作來加鎖或解鎖。

偏向鎖升級成輕量級鎖

  • 線程A請求鎖,發現對象的MarkWord是無鎖狀態,嘗試CAS設置爲偏向鎖狀態,並寫入線程A的ID
  • 線程B也來請求鎖,發現MarkWord已經是偏向鎖狀態,檢查線程A是否存在
  • 如果此時線程A已經不存在
    • 將MarkWord設置爲無鎖狀態(?)
    • 嘗試CAS設置爲偏向鎖狀態,並寫入線程B的ID
  • 如果此時線程A存在
    • 暫停線程A
    • 在線程A的棧幀中創建鎖記錄(Lock Record)
    • 將MarkWord複製到該鎖記錄中
    • 嘗試CAS更新MarkWord,指向該鎖記錄
    • 更新鎖記錄的Owner指向MarkWord
    • 設置MarkWord爲輕量級鎖狀態
    • 此時MarkWord與DisplacedMarkWord存儲了相同的內容
    • 繼續執行線程A
    • 線程B自旋來獲取鎖

輕量級鎖膨脹成重量級鎖

  • 線程A棧幀的鎖記錄已經複製了MarkWord,並且MarkWord指向了該鎖記錄
  • 線程B來請求鎖,發現MarkWord已經是輕量級鎖,嘗試自旋(?)
  • 線程B自旋之後還是獲取不到鎖
    • 更新MarkWord,指向重量級鎖(Mutex Lock)
    • 設置MarkWord爲重量級鎖狀態
    • 阻塞線程B
  • 線程A嘗試CAS用DisplacedMarkWord替換當前的MarkWord,CAS失敗
    • 釋放鎖
    • 喚醒阻塞的線程

 

鎖的優缺點

參考文獻

java併發編程的藝術-方騰飛 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章