AbstractQueuedSynchronizer (AQS) 是 Java 並發包的基石,通過 state 狀態 + FIFO 雙向隊列 + CAS 原子操作 + LockSupport 阻塞喚醒 實現同步器語義。本文以 ReentrantLock 非公平鎖為切入點,深度剖析獨占、非中斷、無超時獲取釋放的核心邏輯。
// 同步狀態 (由子類語義賦予:ReentrantLock=重入次數, Semaphore=許可數, CountDownLatch=計數)
private volatile int state;
// 隊列頭尾節點 (虛擬頭節點 dummy + 真實數據節點)
private volatile Node head;
private volatile Node tail;
// 獨占線程標記
private transient Thread exclusiveOwnerThread;
// 節點定義
static final class Node {
static final Node SHARED = new Node(); // 共享模式標記
static final Node EXCLUSIVE = null; // 獨占模式標記
static final int CANCELLED = 1; // 取消 (超時/中斷)
static final int SIGNAL = -1; // 需喚醒後繼節點
static final int CONDITION = -2; // Condition 隊列節點
static final int PROPAGATE = -3; // 共享模式傳播
volatile int waitStatus; // 節點狀態
volatile Node prev, next; // 雙向指針
volatile Thread thread; // 關聯線程
Node nextWaiter; // Condition 隊列下一節點
}
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 1. 嘗試直接獲取 (子類實現)
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 2. 入隊 + 自旋獲取
selfInterrupt(); // 3. 中斷狀態傳播
}
ReentrantLock.NonfairSync 實現:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) { // 無鎖狀態
if (compareAndSetState(0, acquires)) { // CAS 設置 state
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; // 獲取失敗,需排隊
}
關鍵點:非公平鎖不檢查隊列,直接 CAS 搶佔;公平鎖會先 hasQueuedPredecessors() 檢查是否有前驅節點。
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) { // 快速路徑:尾節點存在
node.prev = pred;
if (compareAndSetTail(pred, node)) { // CAS 更新尾節點
pred.next = node;
return node;
}
}
enq(node); // 慢速路徑:自旋入隊
return node;
}
private Node enq(Node node) {
for (;;) {
Node t = tail;
if (t == null) { // 隊列為空,初始化虛擬頭節點
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
關鍵點:虛擬頭節點 不關聯線程,簡化邊界條件;enq 自旋 CAS 保證線程安全入隊。
final boolean acquireQueued(Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
// 情況 A:前驅是頭節點,嘗試獲取
if (p == head && tryAcquire(arg)) {
setHead(node); // 成為新頭節點
p.next = null; // 斷開舊頭節點引用 (助 GC)
failed = false;
return interrupted;
}
// 情況 B:需阻塞,先調整前驅狀態
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed) cancelAcquire(node); // 異常/中斷退出時取消節點
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) // 前驅已就緒喚醒後繼
return true;
if (ws > 0) { // 前驅已取消,跳過失效節點
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node; // 重新鏈接
} else { // ws == 0 或 PROPAGATE
compareAndSetWaitStatus(pred, ws, Node.SIGNAL); // 設置前驅為 SIGNAL
}
return false; // 重新循環嘗試
}
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); // 阻塞,等待 unpark
return Thread.interrupted(); // 返回並清除中斷狀態
}
public final boolean release(int arg) {
if (tryRelease(arg)) { // 1. 子類嘗試釋放
Node h = head;
if (h != null && h.waitStatus != 0) // 2. 頭節點需喚醒後繼
unparkSuccessor(h);
return true;
}
return false;
}
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; // 返回是否完全釋放
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0) // 清除頭節點狀態
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) { // 後繼為空或已取消,從尾部向前尋找首個有效節點
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread); // 喚醒
}
關鍵點:從尾部向前遍歷而非頭部向後,因 next 指針在併發入隊時可能尚未可見,prev 指針設置較早更可靠。
node.waitStatus = CANCELLEDtail 為前驅有效節點next 指向後繼有效節點unparkSuccessor(node) 確保傳播node.next = node 自引用助 GC| 設計點 | 實現方式 | 優勢 |
|---|---|---|
| 虛擬頭節點 | 初始化空 Node 作為 head | 統一邊界條件,避免頭節點特殊判斷 |
| 雙向隊列 + prev 優先 | 入隊設置 prev,釋放從 tail 向前找後繼 | 併發下 prev 可見性優於 next |
| waitStatus 狀態機 | CANCELLED(1) / SIGNAL(-1) / CONDITION(-2) / PROPAGATE(-3) | 少量狀態編碼豐富同步語義 |
| 自旋 + 阻塞混合 | 前驅為頭節點時自旋 tryAcquire,否則 park | 減少上下文切換,適應短暫持鎖場景 |
| 中斷狀態傳播 | acquire 中斷返回 interrupted,調用者 selfInterrupt() 重新設置 | 不吞噬中斷,保持線程中斷語義 |
| 模板方法模式 | AQS 定義骨架 (acquire/release),子類實現 tryAcquire/tryRelease | 同步邏輯與同步語義解耦 |
tryAcquire 直接 CAS 搶佔,不檢查隊列,吞吐高但可能餓死tryAcquire 先 hasQueuedPredecessors() 檢查 head.next != currentThread,有排隊必進隊列,延遲高但公平源碼閱讀建議:從
ReentrantLock.lock() → sync.acquire(1) → AQS.acquire()入口,重點掌握acquireQueued自旋邏輯、shouldParkAfterFailedAcquire狀態機、unparkSuccessor喚醒策略、cancelAcquire失效清理。畫時序圖輔助理解併發場景下指針變化。