JVM 眼中的共享數據:只關心兩類
Java 內存模型(JMM)規定:線程間共享數據僅存在於堆與方法區:
- 實例字段:堆中對象內部
- 靜態字段:方法區中
Class 對象內部
棧幀(局部變量、操作數棧)線程私有,**無需同步**。
監視器:JVM 同步的核心抽象
每個對象/類邏輯關聯一個監視器:
- 對象監視器:保護實例字段
- 類監視器:保護靜態字段(實際鎖住
Class 對象)
無實例字段的對象、無靜態字段的類,監視器空轉(無保護目標)。
鎖機制:排他性訪問的物理實現
對象鎖
- 每個對象頭包含 Mark Word(存儲鎖狀態、哈希碼、GC 年齡、線程 ID)
- 輕量級鎖 → 膨脹為重量級鎖(操作系統互斥量)
- 偏向鎖(JDK 15 移除):無競爭時避免 CAS
類鎖本質
// 等價代碼
synchronized (MyClass.class) { ... } // 鎖住 Class 對象
static synchronized void method() { ... } // 隱式鎖住 Class 對象
可重入性:鎖計數器
- 同一線程多次獲取同一鎖,計數器 +1
- 釋放時 -1,歸零才真正釋放
- 實現遞歸同步、父子類同步調用
synchronized 語義:JVM 自動加鎖/解鎖
// 同步代碼塊
synchronized (lockObject) {
// 1. 進入:monitorenter 指令 → 嘗試獲取鎖
// 成功:計數器=1,繼續執行
// 失敗:進入阻塞隊列,線程掛起
// 2. 執行業務邏輯
// 3. 退出:monitorexit 指令 → 計數器-1
// 歸零:喚醒阻塞隊列線程
// 異常退出:JVM 自動執行 monitorexit(保證鎖釋放)
}
Java 內存模型 (JMM) 與同步的雙重保障
同步的兩大語義
- 互斥性:同一時間只有一線程執行同步塊
- 可見性:解鎖前對共享變量的寫入,對後續獲取同一鎖的線程可見
Happens-Before 規則(同步相關)
- 監視器鎖規則:解鎖 hb 後續加鎖
- volatile 變量規則:寫入 hb 後續讀取
- 線程啟動規則:
Thread.start() hb 線程內動作
- 線程終止規則:線程內動作 hb
Thread.join() 返回
鎖優化演進:從重量級到適應性
| 階段 | JDK 版本 | 特徵 | 適用場景 |
| 重量級鎖 | 早期 | OS Mutex,內核態切換開銷大 | 高競爭、長關鍵區 |
| 輕量級鎖 | 1.6+ | CAS 操作 Mark Word,無競爭無內核態 | 低競爭、短關鍵區 |
| 偏向鎖 | 1.6-14 | 無競爭時綁定線程 ID,後續無 CAS | 單線程反復訪問 |
| 適應性自旋 | 1.6+ | 自旋等待釋放,避免阻塞喚醒開銷 | 極短關鍵區、多核 |
| 鎖消除 | JIT 階段 | 逃逸分析發現無共享,直接移除同步 | 局部對象、StringBuffer |
| 鎖粗化 | JIT 階段 | 合併相邻同步塊,減少鎖操作 | 循環內同步 |
類鎖 vs 對象鎖:常見誤區
class Demo {
// 鎖住 Demo.class
public static synchronized void staticMethod() { ... }
// 鎖住 this (具體實例)
public synchronized void instanceMethod() { ... }
// 顯式類鎖
public void explicitClassLock() {
synchronized (Demo.class) { ... }
}
// 顯式實例鎖
public void explicitInstanceLock() {
synchronized (this) { ... }
}
}
// 結論:staticMethod 與 instanceMethod 可並發執行(鎖不同對象)
// explicitClassLock 與 staticMethod 互斥(同一 Class 對象)
最佳實踐指南
- 優先使用
java.util.concurrent:ReentrantLock, ConcurrentHashMap, LongAdder, BlockingQueue
- 同步塊最小化:僅包裝共享狀態訪問,業務邏輯移出
- 避免鎖活動對象:
String, Integer, 箱類型可能被複用
- 死鎖預防:固定獲取順序、
tryLock(timeout)、避免嵌套鎖
- 讀寫分離:
ReentrantReadWriteLock、StampedLock(樂觀讀)
- 無鎖編程:
Atomic*, VarHandle, LongAdder(高併發計數)
總結
Java 多線程同步在 JVM 層面是監視器鎖對共享堆/方法區數據的互斥訪問控制,配合 JMM 的 Happens-Before 語義保證可見性。理解:
- 對象頭 Mark Word 鎖狀態機
- 類鎖本質鎖
Class 對象
- 可重入計數器機制
- JIT 鎖優化(消除、粗化、偏向、輕量級)
- JMM 與同步的雙重保障
現代開發應拥抱高級併發組件,將同步細節委託給經過驗證的庫,專注於業務邏輯的線程安全設計。
- 上一篇文章: 高效 Java 編程核心原則:從對象創建到併發安全的實戰指南
- 下一篇文章: Java 值傳遞機制深度解析:為什麼說 Java 只有值傳遞
Copyright ©
程式碼之家 All Rights Reserved