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

JDK 1.8 AbstractQueuedSynchronizer 獨占鎖實現深度解析(上)

     編輯:Java 相關知識

AQS (AbstractQueuedSynchronizer) 是 JDK 並發包核心基石,支援獨占/共享兩種同步模式。本文以 ReentrantLock 為切入點,深度剖析獨占鎖實現:狀態變數、CLH 隊列、CAS 自旋、公平/非公平策略、Condition 協作。

ReentrantLock 核心結構


public class ReentrantLock implements Lock {
    private final Sync sync;
    abstract static class Sync extends AbstractQueuedSynchronizer { ... }
    static final class NonfairSync extends Sync { ... }
    static final class FairSync extends Sync { ... }
}

獨占鎖獲取流程

lock() → acquire(1)


public final void acquire(int arg) {
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

tryAcquire 由子類實現 (FairSync 範例)


protected final boolean tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

入隊:addWaiter(Node.EXCLUSIVE)

  • 建立節點封裝當前線程,類型為 EXCLUSIVE
  • CAS 快速入隊尾部,失敗進 enq() 自旋入隊

排隊阻塞:acquireQueued


final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null;
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed) cancelAcquire(node);
    }
}

CLH 隊列結構

  • 頭節點 = 持有鎖線程(或哨兵節點)
  • 後續節點 = 等待線程,FIFO 順序
  • 節點欄位:thread、waitStatus、prev/next
  • waitStatus:SIGNAL(-1) 需喚醒後繼、CANCELLED(1) 取消、CONDITION(-2) 條件等待、PROPAGATE(-3) 共享傳播

釋放鎖流程

unlock() → release(1)


public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

tryRelease (FairSync)


protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

喚醒後繼:unparkSuccessor

  • 從尾部向前尋找第一個 waitStatus < 0 節點
  • LockSupport.unpark(thread) 喚醒

公平 vs 非公平鎖

策略lock() 行為tryAcquire 邏輯
公平直接進 AQS 流程!hasQueuedPredecessors() 檢查隊列
非公平先 CAS 搶佔 state成功直接返回,失敗再進 AQS

// 非公平鎖 lock()
final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

核心設計哲學

  • 模板方法:AQS 定義骨架 (acquire/release),子類實現規則 (tryAcquire/Release)
  • 狀態抽象:state 可表鎖計數、信號量許可、倒數閂計數
  • 隊列統一:CLH 隊列承載獨占/共享/條件節點
  • 非阻塞入隊:CAS + 自旋避免鎖競爭
  • 阻塞原語:LockSupport.park/unpark 基於 UNSAFE/JNI

衍生應用

  • ReentrantReadWriteLock:同隊列混合獨占(寫)/共享(讀)節點
  • FutureTask (JDK 1.6):獨占 API 實現結果獲取阻塞
  • Semaphore:共享 API 實現許可證控制
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved