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

JDK 1.8 AbstractQueuedSynchronizer 共享鎖實現深度解析(下)

     編輯:Java 相關知識

承接上篇獨占鎖分析,本文以 CountDownLatch 為切入點,深度剖析 AQS 共享鎖實現機制:節點類型區分、狀態傳播、自旋優化與超時控制。

CountDownLatch 核心流程

初始化:狀態即計數器


public CountDownLatch(int count) {
    sync = new Sync(count);
}
static final class Sync extends AbstractQueuedSynchronizer {
    Sync(int count) { setState(count); }  // state = 計數器初值
}

await():獲取共享鎖


public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

調用鏈:acquireSharedInterruptibly → tryAcquireShared → doAcquireSharedInterruptibly

tryAcquireShared 判斷邏輯


protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;  // state==0 成功,否則失敗
}

doAcquireSharedInterruptibly 入隊與自旋


private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
    final Node node = addWaiter(Node.SHARED);  // 標記為共享節點
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquireShared(arg) >= 0) {
                setHeadAndPropagate(node, arg);  // 關鍵:傳播喚醒
                p.next = null;
                failed = false;
                return;
            }
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed) cancelAcquire(node);
    }
}

共享鎖獨特機制:狀態傳播

setHeadAndPropagate


private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head;
    setHead(node);  // CAS 更新頭節點
    if (propagate > 0 || h == null || h.waitStatus < 0 ||
        (h = head) == null || h.waitStatus < 0) {
        Node s = node.next;
        if (s == null || s.isShared())
            doReleaseShared();  // 喚醒下一個共享節點
    }
}

doReleaseShared:傳播鏈路


private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;
                unparkSuccessor(h);  // 喚醒後繼節點
            } else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;
        }
        if (h == head) break;
    }
}

傳播示意

  1. Node1 獲取鎖成功 → 成為頭節點 → unparkSuccessor 喚醒 Node2
  2. Node2 線程醒來 → 重試 tryAcquireShared → 成功 → 成為頭節點 → 喚醒 Node3
  3. 以此類推,實現「多線程同時等待、同時被喚醒、同時返回」

countDown():釋放共享鎖


public void countDown() {
    sync.releaseShared(1);
}
protected boolean tryReleaseShared(int releases) {
    for (;;) {
        int c = getState();
        if (c == 0) return false;
        if (compareAndSetState(c, c - releases))  // CAS 遞減
            return c - releases == 0;  // 歸零返回 true 觸發傳播
    }
}

共享鎖 API 族系

方法特性
acquireShared忽略中斷
acquireSharedInterruptibly響應中斷
tryAcquireShared非阻塞嘗試
acquireSharedNanos超時控制(含自旋閾值 1000ns)

自旋優化細節


if (nanosTimeout > spinForTimeoutThreshold)  // 1000ns
    LockSupport.parkNanos(this, nanosTimeout);
else
    // 短超時直接自旋,避免上下文切換開銷

AQS 設計哲學總結

  • 模板方法模式:骨架在 AQS,規則(tryAcquire/Release)由子類定義
  • 狀態抽象化:state 可代表鎖計數、信號量許可、倒數閂計數、讀寫鎖狀態
  • 隊列統一化:同一雙向隊列承載獨占/共享/條件節點
  • 工具類組合:ReentrantReadWriteLock 同時使用獨占(寫)與共享(讀) API

衍生應用

  • FutureTask (JDK 1.6) 使用獨占 API 實現結果獲取阻塞
  • Semaphore 使用共享 API 實現許可證控制
  • CyclicBarrier 組合鎖與條件實現攔截點
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved