synchronized 是Java併發程式設計中非常重要的角色,這裡簡單記錄對於其原理的學習
基礎內容
-
synchronized 具有互斥性,可以保證只有一個執行緒可以訪問同步塊
-
synchronized 修飾普通方法,鎖為當前例項物件
-
synchronized 修飾靜態方法,鎖為當前類的Class物件
-
synchronized 修飾同步方法塊,鎖為程式碼塊括號中填寫的物件
指令實現
- 同步程式碼塊通過
monitorenter
和monitorexit
兩條指令控制同步程式碼塊的訪問 - 同步方法通過設定
ACC_SYNCHRONIZED
標誌符控制(也可以通過上述兩條指令來實現)
所有Java物件都會有一個monitor,當monitor被持有後,物件就處於鎖定狀態。當執行到monitorenter指令時,執行緒會嘗試獲取物件的monitor,而執行monitorexit後會釋放物件的monitor
Java SE 1.6為了降低鎖的獲取、釋放造成的效能損耗,增加了偏向鎖、輕量級鎖。鎖的級別由高到低依次為無鎖狀態、偏向鎖、輕量級鎖、重量級鎖,鎖可以升級,但不可以降級
偏向鎖
內容
鎖由同一執行緒多次獲取時,會在物件頭中記錄執行緒ID,並在之後對同步塊的訪問時,不需要進行CAS操作來加鎖、解鎖。
Mark Word狀態
Mark Word記錄於物件頭,儲存物件的hashcode或鎖資訊
這裡補充無鎖狀態的Mark Word狀態
鎖狀態 | hashcode | 分代年齡 | 是否偏向鎖 | 鎖標誌位 |
---|---|---|---|---|
無鎖狀態 | 物件的hashcode | 物件分代年齡 | 0 | 01 |
偏向鎖狀態的Mark Word狀態
鎖狀態 | Thread ID | epoch | 分代年齡 | 是否偏向鎖 | 鎖標誌位 |
---|---|---|---|---|---|
偏向鎖 | 記錄指向的執行緒id | epoch | 物件分代年齡 | 1 | 01 |
加鎖與解鎖
輕量級鎖
內容
輕量級鎖是通過自旋實現非阻塞同步,屬於樂觀鎖,可以膨脹為重量級鎖
Mark Word狀態
鎖狀態 | 記錄 | 鎖標誌位 |
---|---|---|
輕量級鎖 | 指向棧中鎖記錄的指標 | 00 |
加鎖與解鎖
加鎖
解鎖
重量級鎖
內容
鎖升級為重量級鎖後,其他試圖獲取鎖的執行緒均會被阻塞,等待持有鎖的執行緒釋放鎖後,被喚醒的執行緒會開始競爭獲取鎖。
重量級鎖是一種互斥鎖,其依賴於物件內部的monitor鎖實現,在作業系統層面是通過MutexLock實現的。雖然在阻塞時不需要耗費CPU資源,但是執行緒從阻塞狀態喚醒需要作業系統完成狀態轉換(使用者態到核心態),耗時較長。
Mark Word狀態
鎖狀態 | 記錄 | 鎖標誌位 |
---|---|---|
重量級鎖 | 指向互斥量(重量級鎖)的指標 | 10 |
比較
對比表參考《Java併發程式設計的藝術》
鎖 | 優點 | 缺點 | 適用場景 |
---|---|---|---|
偏向鎖 | 加鎖和解鎖不需要額外的消耗,和執行非同步方法相比僅存在納秒級的差距 | 如果執行緒間存在鎖競爭,會帶來額外的鎖撤銷的消耗 | 適用於只有一個執行緒訪問同步塊的場景 |
輕量級鎖 | 非阻塞同步,提高程式響應速度 | 自旋消耗CPU資源 | 追求響應時間,同步方法執行速度較快 |
重量級鎖 | 執行緒阻塞時不需要消耗CPU | 執行緒阻塞,響應時間緩慢 | 追求吞吐量,同步塊執行時間較長 |
參考文章
- 《Java併發程式設計的藝術》2.2 synchronized的實現原理與應用
- 執行緒安全(上)--徹底搞懂synchronized(從偏向鎖到重量級鎖)
- Java併發程式設計:Synchronized底層優化(偏向鎖、輕量級鎖)
如有問題,還請指出