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

Java 併發鎖機制深度解析:偏向鎖、輕量級鎖、重量級鎖與自旋鎖演進

     編輯:Java 程式設計入門

隨著高併發場景普及,JVM 鎖優化機制成為 Java 開發者必備知識。本文系統梳理偏向鎖、輕量級鎖、重量級鎖、自旋鎖四大鎖類型的原理、升級流程與適用場景,並附帶 JMH 風格效能測試程式碼。

鎖升級流程總覽

JDK 1.6 引入鎖升級機制,鎖狀態隨併發程度動態轉換:

無鎖 → 偏向鎖 → 輕量級鎖 → 重量級鎖

核心目標:無競爭時零同步開銷,低競爭時用戶態自旋,高競爭時OS層面阻塞。

1. 偏向鎖

核心思想

「一個線程反覆獲得同一把鎖,無其他線程競爭時,省去 CAS 操作」。首次獲得鎖時在對象頭 Mark Word 記錄線程 ID,後續同線程進入同步塊僅需檢查 ID 一致即可,無需 CAS。

啟用與禁用

-XX:+UseBiasedLocking          # 預設開啟(JDK 1.6+)
-XX:-UseBiasedLocking           # 禁用(高併發場景建議關閉)

效能實測

單線程循環 1000 萬次操作 Vector(同步容器):

配置耗時(ms)說明
開啟偏向鎖~120同步開銷近似 ArrayList
關閉偏向鎖~350每次需 CAS 競爭鎖

測試程式碼:

public class BiasedLockTest {
    static List list = new Vector<>();
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10_000_000; i++) list.add(i);
        System.out.println(System.currentTimeMillis() - start);
    }
}

適用場景與避坑

  • ✅ 單線程或極低併發訪問同步塊
  • ❌ 高併發、多線程輪流搶鎖 → 頻繁撤銷偏向、升級開銷大,反而拖慢效能

2. 輕量級鎖

觸發條件

偏向鎖失敗(有其他線程嘗試獲取鎖)時升級。

實現原理

  • 線程棧幀分配 BasicObjectLock(包含 BasicLock + 對象指標)
  • CAS 將對象頭 Mark Word 替換為指向 BasicLock 的指標(標記位 00)
  • 原 Mark Word 備份至 BasicLock.displaced_header
  • 鎖釋放時 CAS 恢復 Mark Word

優勢:無 OS 內核切換,用戶態 CAS 完成加解鎖,適合「短時間持鎖、低併發」場景。

3. 重量級鎖

觸發條件

輕量級鎖自旋失敗(JDK 1.6 預設自旋 10 次),或鎖被其他線程長時間持有。

實現原理

  • Mark Word 指向 ObjectMonitor(C++ 實現,基於 OS Mutex)
  • 線程進入 _WaitSet 或 _EntryList 排隊,OS 掛起/喚醒
  • 標記位 10,標識 monitor 狀態

代價:用戶態↔內核態切換、線程上下文切換,延遲微秒級以上。

4. 自旋鎖

核心邏輯

線程不立即阻塞,執行空循環(PAUSE 指令)等待鎖釋放。若自旋期間鎖釋放,直接獲得,避免阻塞開銷。

參數演進

  • JDK 1.6:-XX:+UseSpinning 開啟,-XX:PreBlockSpin=10 設定次數
  • JDK 1.7+:參數廢棄,JVM 自適應調整自旋次數(根據歷史自旋成功率、CPU 核心數等)

適用性判斷

場景自旋效果
鎖持有時間極短、併發低✅ 顯著降低阻塞開銷
鎖持有時間長、併發高❌ 白白消耗 CPU,最終仍需阻塞

總結:鎖選型決策樹

單線程/極低併發 → 偏向鎖(預設開啟)
     ↓ 有競爭
低併發、短持鎖 → 輕量級鎖(CAS)
     ↓ 自旋失敗/長持鎖
高併發/長持鎖 → 重量級鎖(OS Mutex)

實戰建議:縮小同步塊範圍、降低鎖粒度、使用無鎖資料結構(CAS、ThreadLocal) 才是根本優化之道。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved