Java對象頭
Synchronized用的鎖是存在java對象頭裏的。如果對象是數組,虛擬機則使用三個字寬(Word)存儲對象頭,如果是非數組類型則用2個字寬存儲,在32位虛擬機 1個字寬=4個字節
鎖狀態 | 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併發編程的藝術-方騰飛