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

Java 併發鎖機制深度解析:阻塞鎖原理、CLH 佇列鎖與 LockSupport 實戰

     編輯:Java 相關知識

鎖分類與阻塞鎖定位

Java 鎖按線程狀態變化分類:

  • 自旋鎖:線程保持 RUNNABLE,CPU 空轉等待(CAS 循環),適合極短關鍵區
  • 阻塞鎖:線程進入 BLOCKED/WAITING 狀態,釋放 CPU,適合長關鍵區或高競爭

Java 線程六大狀態:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。阻塞鎖使線程從 RUNNABLE → BLOCKED/WAITING,喚醒後回 RUNNABLE 參與調度。

核心阻塞鎖實作機制

CLH 佇列鎖:自旋轉阻塞的經典演進

CLH (Craig, Landin, Hagersten) 鎖以隱式佇列節點實現公平鎖,每個節點僅自旋等待前驅節點釋放。Java 版本常結合 LockSupport 將自旋改為阻塞:

public class CLHLock {
    static class Node {
        volatile Thread thread; // 當前持有鎖的線程
    }

    private final ThreadLocal myNode = ThreadLocal.withInitial(Node::new);
    private final AtomicReferenceFieldUpdater tailUpdater =
        AtomicReferenceFieldUpdater.newUpdater(CLHLock.class, Node.class, "tail");
    @SuppressWarnings("unused") private volatile Node tail;

    public void lock() {
        Node node = myNode.get();
        Node pred = tailUpdater.getAndSet(this, node); // 入隊
        if (pred != null) {
            pred.thread = Thread.currentThread(); // 通知前驅喚醒我
            LockSupport.park(this);               // 阻塞等待
            pred.thread = null;
        }
    }

    public void unlock() {
        Node node = myNode.get();
        if (!tailUpdater.compareAndSet(this, node, null)) { // 隊列還有後繼
            LockSupport.unpark(node.thread); // 喚醒後繼
        }
        myNode.remove(); // 清理 ThreadLocal
    }
}

關鍵設計點

  1. ThreadLocal 節點:每線程固定節點,避免重複分配
  2. AtomicReferenceFieldUpdater:無鎖 CAS 更新尾指標
  3. 前驅通知模式:前驅節點持有後繼線程引用,釋放時直接 unpark
  4. 許可證語義:park/unpark 不會丟失喚醒信號(許可證最多 1),優於 wait/notify

阻塞鎖 vs 自旋鎖:效能抉擇

機制關鍵 API適用層級特點
synchronized監視器鎖 (Object Monitor)JVM 內建重量級鎖 → 鎖升級(偏向→輕量→重量)
ReentrantLockAQS (AbstractQueuedSynchronizer)JUC公平/非公平、可中斷、條件變數
Object.wait/notify監視器等待集合JVM 內建需在 synchronized 區塊內使用
LockSupport.park/unpark線程阻塞/喚醒原語JUC 基礎設施不需鎖對象、可指定阻塞器、支援許可證語義

實戰建議

  1. 優先使用高層抽象:ReentrantLock、ConcurrentHashMap、LongAdder 等 JUC 組件
  2. 避免手寫底層鎖:除非造輪子或極致效能調優,否則 CLH、MCS 等留給框架作者
  3. 監控鎖競爭:jstack 分析 BLOCKED 線程、Java Flight Recorder 記錄鎖事件
  4. 鎖粒度與拆分:縮小關鍵區、分段鎖、無鎖資料結構(CAS、StampedLock)

總結

阻塞鎖透過 LockSupport.park/unpark 實現線程掛起與喚醒,是 Java 併發套件的基石。理解其原理有助於診斷死鎖、活鎖、優化高競爭場景。日常開發請善用 JUC 提供的成熟組件,僅在框架開發或極端效能場景深入底層。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
場景推薦鎖類型理由
關鍵區極短(< 1μs)、低競爭自旋鎖 / CAS避免上下文切換開銷
關鍵區較長、高競爭阻塞鎖釋放 CPU、避免總線風暴
需公平性、條件變數ReentrantLockAQS 佇列天然公平、支援 Condition
簡單同步、程式碼簡潔synchronizedJVM 優化成熟、偏向鎖適合單線程