synchronized 是 Java 並發編程的基石。本文用「房子-房間-鑰匙」隱喻解構監視器鎖模型,深度剖析鎖升級 (偏向/輕量/重量)、JMM 內存語義、鎖消除/粗化等 JVM 優化技術。
核心規則: 想進上鎖房間 → 必須拿到鑰匙 → 用完立即還鑰匙 (「隨用隨借,用完即還」)。同一時間只能有一人持有鑰匙。
public class SyncDemo {
private final Object lock = new Object(); // 顯式鎖對象
// 1. 同步實例方法:鎖對象 = this (當前實例)
public synchronized void instanceMethod() { ... }
// 2. 同步靜態方法:鎖對象 = Class 對象 (SyncDemo.class)
public static synchronized void staticMethod() { ... }
// 3. 同步代碼塊:可指定任意鎖對象,粒度最細
public void blockSync() {
synchronized (lock) { // 鎖住 lock 對象,不影響 this 其他同步方法
// 臨界區代碼
}
synchronized (OtherClass.class) { // 甚至可鎖其他類 Class 對象 (跨類同步)
// 另一個臨界區
}
}
}
| 維度 | 同步方法 | 同步代碼塊 |
|---|---|---|
| 鎖對象 | 隱性 (this / Class) | 顯性指定 (任意對象) |
| 鎖粒度 | 整個方法 | 可精確到幾行代碼 |
| 靈活性 | 低 | 高 (可跨對象/類同步) |
| 效能影響 | 大 (鎖住整方法) | 小 (僅鎖臨界區) |
| 死鎖風險 | 較高 (this 易被外部鎖) | 可控 (私有 final lock 對象) |
Java 6 引入鎖升級機制,從無鎖 → 偏向鎖 → 輕量級鎖 → 重量級鎖,單向升級,不可降級。
| 鎖狀態 | 適用場景 | 核心機制 | 優缺點 | 標記位 (Mark Word) |
|---|---|---|---|---|
| 無鎖 | 無並發訪問 | 無同步開銷 | 最快,但無安全保障 | 01 (無鎖標記) |
| 偏向鎖 | 單執行緒重複訪問 | CAS 設置 ThreadID 到 Mark Word,後續進出無 CAS | 極快 (無 CAS),但撤銷成本高 | 01 (偏向鎖標記) + ThreadID |
| 輕量級鎖 | 多執行緒交替訪問 (低競爭) | CAS 將 Mark Word 複製到棧幀 Lock Record,CAS 成功即獲鎖 | 無內核態切換,自旋等待 | 00 (輕量級鎖標記) + 指向 Lock Record |
| 重量級鎖 | 高競爭、長臨界區 | ObjectMonitor 監視器,OS 互斥量,阻塞掛起 | 效能最低,但公平、可靠 | 10 (重量級鎖標記) + 指向 ObjectMonitor |
// 無鎖狀態
| 25bit 哈希碼 | 4bit 分代年齡 | 1bit 無鎖標記(0) | 2bit 鎖標記(01) |
// 偏向鎖狀態
| 23bit ThreadID | 2bit Epoch | 4bit 分代年齡 | 1bit 偏向標記(1) | 2bit 鎖標記(01) |
// 輕量級鎖狀態
| 62bit 指向棧幀 Lock Record 的指針 | 2bit 鎖標記(00) |
// 重量級鎖狀態
| 62bit 指向 ObjectMonitor 的指針 | 2bit 鎖標記(10) |
synchronized (lock) {
// 持有鎖
Thread.sleep(1000); // 睡眠期間 **繼續持有鎖**!其他線程無法進入
// 做完業務
} // 這裡才釋放鎖
原因: sleep() 不釋放監視器鎖,只有 wait() 會釋放鎖並進入等待隊列。
new Object()),直接移除同步ReentrantLock (公平鎖、可中斷、Condition)StampedLock 樂觀讀 / CopyOnWriteArrayList理解鎖升級、Mark Word、JMM 語義,才能在高併發系統中寫出既正確又高效的同步代碼。