首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 多線程同步的 JVM 視角:鎖、監視器與內存模型深度解析

     編輯:Java 相關知識

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) 與同步的雙重保障

同步的兩大語義

  1. 互斥性:同一時間只有一線程執行同步塊
  2. 可見性:解鎖前對共享變量的寫入,對後續獲取同一鎖的線程可見

Happens-Before 規則(同步相關)

  • 監視器鎖規則:解鎖 hb 後續加鎖
  • volatile 變量規則:寫入 hb 後續讀取
  • 線程啟動規則:Thread.start() hb 線程內動作
  • 線程終止規則:線程內動作 hb Thread.join() 返回

鎖優化演進:從重量級到適應性

類鎖 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 對象)

最佳實踐指南

  1. 優先使用 java.util.concurrent:ReentrantLock, ConcurrentHashMap, LongAdder, BlockingQueue
  2. 同步塊最小化:僅包裝共享狀態訪問,業務邏輯移出
  3. 避免鎖活動對象:String, Integer, 箱類型可能被複用
  4. 死鎖預防:固定獲取順序、tryLock(timeout)、避免嵌套鎖
  5. 讀寫分離:ReentrantReadWriteLock、StampedLock(樂觀讀)
  6. 無鎖編程:Atomic*, VarHandle, LongAdder(高併發計數)

總結

Java 多線程同步在 JVM 層面是監視器鎖對共享堆/方法區數據的互斥訪問控制,配合 JMM 的 Happens-Before 語義保證可見性。理解:

  1. 對象頭 Mark Word 鎖狀態機
  2. 類鎖本質鎖 Class 對象
  3. 可重入計數器機制
  4. JIT 鎖優化(消除、粗化、偏向、輕量級)
  5. JMM 與同步的雙重保障

現代開發應拥抱高級併發組件,將同步細節委託給經過驗證的庫,專注於業務邏輯的線程安全設計。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
階段JDK 版本特徵適用場景
重量級鎖早期OS Mutex,內核態切換開銷大高競爭、長關鍵區
輕量級鎖1.6+CAS 操作 Mark Word,無競爭無內核態低競爭、短關鍵區
偏向鎖1.6-14無競爭時綁定線程 ID,後續無 CAS單線程反復訪問
適應性自旋1.6+自旋等待釋放,避免阻塞喚醒開銷極短關鍵區、多核
鎖消除JIT 階段逃逸分析發現無共享,直接移除同步局部對象、StringBuffer
鎖粗化JIT 階段合併相邻同步塊,減少鎖操作循環內同步