Java 並發編程:Synchronized 底層優化機制深度解析
synchronized 通過對象頭的監視器鎖實現,底層依賴 OS 的 Mutex Lock,用戶態/核心態切換開銷大,稱為「重量級鎖」。JDK 1.6 引入「輕量級鎖」和「偏向鎖」優化。
鎖狀態與對象頭標記 (32位 JVM)
| 鎖狀態 | 標記位 | 是否偏向鎖 | 對象頭內容 |
| 無鎖 | 01 | 0 | 對象 hashCode + 分代年齡 |
| 偏向鎖 | 01 | 1 | 線程 ID + Epoch + 分代年齡 |
| 輕量級鎖 | 00 | - | 指向棧中鎖記錄的指針 |
| 重量級鎖 | 10 | - | 指向互斥量的指針 |
| GC 標記 | 11 | - | 空 |
輕量級鎖
適用場景:線程交替執行同步塊,無競爭。
加鎖流程
- 進入同步塊,對象無鎖 → 當前線程棧幀建立「鎖記錄」,複製對象 Mark Word 為「Displaced Mark Word」。
- CAS 更新對象 Mark Word 指向鎖記錄,成功則獲得鎖,標記位設為 00。
- CAS 失敗 → 檢查 Mark Word 是否指向當前線程棧幀(重入),否則升級為重量級鎖,標記位 10,其他線程阻塞並自旋嘗試獲取。
解鎖流程
- CAS 將 Displaced Mark Word 替換回對象 Mark Word。
- 成功 → 同步結束。失敗 → 鎖已膨脹,喚醒被掛起線程。
偏向鎖
適用場景:無多線程競爭,同一線程反覆進入同步塊。消除 CAS 開銷,僅需一次 CAS 設置 ThreadID。
獲取流程
- 檢查 Mark Word 偏向鎖標識為 1、鎖標記 01 → 可偏向狀態。
- ThreadID 指向當前線程 → 直接進入同步塊。
- 否則 CAS 競爭鎖,成功設 ThreadID,失敗表示有競爭 → 撤銷偏向鎖,升級輕量級鎖。
撤銷機制
遇其他線程競爭時,在全局安全點掛起持有線程,恢復到無鎖或輕量級鎖狀態。線程不會主動釋放偏向鎖。
三種鎖轉換關係
無鎖 → 偏向鎖 → 輕量級鎖 → 重量級鎖(單向升級,不降級)
其他 JVM 優化
- 適應性自旋:自旋成功增加次數,失敗減少次數,避免 CPU 空轉。
- 鎖粗化:連續加鎖/解鎖合併為一次大範圍鎖(如 StringBuilder 連續 append)。
- 鎖消除:逃逸分析發現數據不逃逸出線程,判定為線程安全,自動刪除鎖(如方法內部 StringBuffer)。
鎖選型對比表
| 鎖類型 | 優點 | 缺點 | 適用場景 |
| 偏向鎖 | 加/解鎖無額外開銷,與非同步方法差距納秒級 | 有競爭時撤銷開銷大 | 單線程訪問同步塊 |
| 輕量級鎖 | 競爭線程不阻塞,提升響應速度 | 自旋消耗 CPU | 追求響應時間,同步塊執行極快 |
| 重量級鎖 | 不自旋不消耗 CPU | 線程阻塞,響應慢 | 追求吞吐量,同步塊執行較長 |
激烈競爭下偏向鎖反而降低效能,可用 -XX:-UseBiasedLocking 禁用。