一、什么是 AQS?
AbstractQueuedSynchronizer
(簡稱 AQS)是 Java 并發包 java.util.concurrent.locks
中的一個核心同步框架,用于構建鎖和同步器,如:
- ReentrantLock
- ReentrantReadWriteLock
- CountDownLatch
- Semaphore
- FutureTask
AQS 通過一個FIFO 雙向等待隊列(CLH 隊列)管理線程的同步狀態,使開發者可以專注于同步邏輯,而不必關注線程的調度、阻塞、喚醒等底層細節。
二、AQS 的核心設計
1. 核心成員變量
private volatile int state; // 同步狀態
private transient Node head; // 隊列頭節點
private transient Node tail; // 隊列尾節點
state
:用來表示資源的占用狀態(例如是否被鎖定、可用信號量數等);head
和tail
:維護一個 CLH 等待隊列,用于管理阻塞的線程。
2. 核心方法(模板方法)
AQS 提供一系列模板方法用于子類實現:
// 共享模式
protected int tryAcquireShared(int arg);
protected boolean tryReleaseShared(int arg);// 獨占模式
protected boolean tryAcquire(int arg);
protected boolean tryRelease(int arg);
子類需要實現這些方法,以控制對資源的獲取與釋放邏輯。
三、AQS 工作流程
1. 獲取鎖(以獨占模式為例)
lock.lock() → tryAcquire() 嘗試獲取 → 獲取失敗 → 加入 CLH 隊列 → park(阻塞)→ 被喚醒時再次嘗試
流程:
- 調用
tryAcquire()
嘗試獲取資源; - 如果失敗,則將當前線程封裝為
Node
加入等待隊列; - 阻塞(park)當前線程;
- 資源釋放后,喚醒下一個等待線程。
2. 釋放鎖
unlock() → tryRelease() 成功 → 喚醒隊列中下一個線程
tryRelease()
將state
設置為 0;- 然后調用
unparkSuccessor()
喚醒下一個線程。
四、獨占模式 vs 共享模式
模式 | 描述 |
---|---|
獨占模式 | 同一時刻只能有一個線程訪問,如 ReentrantLock |
共享模式 | 多個線程可以共享資源,如 Semaphore、CountDownLatch |
AQS 區分這兩種模式,并分別處理入隊、出隊、喚醒等邏輯。
五、CLH 隊列機制
AQS 使用一種變體的 CLH(Craig–Landin–Hagersten)同步隊列 實現線程排隊。
隊列結構:
head -> Node1 -> Node2 -> ... -> tail
- 每個節點是一個線程的等待快照;
- 新線程失敗后加入尾部,前驅釋放時喚醒后繼;
- 自旋或 park 阻塞等待。
六、典型用法示例
自定義鎖的基本寫法
class MyLock extends AbstractQueuedSynchronizer {protected boolean tryAcquire(int arg) {return compareAndSetState(0, 1);}protected boolean tryRelease(int arg) {setState(0);return true;}public void lock() {acquire(1); // 內部調用 tryAcquire + 入隊邏輯}public void unlock() {release(1);}
}
CountDownLatch 原理
- 使用共享模式;
- 每次
countDown()
執行releaseShared(-1)
; await()
會在 state = 0 前阻塞。
七、AQS 的優點
優點 | 說明 |
---|---|
封裝阻塞邏輯 | 使用 LockSupport 封裝了 park/unpark |
可復用性強 | 模板方法模式,便于自定義同步器 |
隊列高效 | FIFO 隊列實現公平性,性能穩定 |
支持兩種模式 | 支持共享和獨占資源控制 |
八、注意點 & 問題排查
- 死鎖:一定要在
try...finally
中釋放鎖; - state 狀態:設計時需合理設置 state 的含義(計數、二進制等);
- CLH 隊列泄露:釋放時未正確調用
unpark
會導致阻塞線程永久等待; - 性能瓶頸:高并發下需注意鎖競爭,考慮使用
StampedLock
或樂觀鎖優化。
九、AQS 應用類匯總
類名 | 模式 | 簡介 |
---|---|---|
ReentrantLock | 獨占 | 可重入、可中斷、公平/非公平 |
Semaphore | 共享 | 控制資源訪問數目 |
CountDownLatch | 共享 | 等待所有任務完成 |
ReentrantReadWriteLock | 共享 + 獨占 | 高效讀寫分離 |
FutureTask | 獨占 | 控制異步任務狀態 |
AbstractQueuedSynchronizer | 基類 | 框架級同步器 |
十、總結
- AQS 是構建 Java 同步工具的核心;
- 通過隊列管理線程阻塞與喚醒;
- 使用模板方法封裝了多種模式;
- 掌握 AQS = 掌握 Java 并發的核心原理。
補充:AQS 源碼流程圖解析 + ReentrantLock 實戰源碼
一、AQS 獲取鎖(acquire)源碼流程
public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();
}
流程圖解:
acquire()│┌───────┴────────┐↓ ↓tryAcquire() // 自定義嘗試獲取(成功返回true) (失敗)↓ ↓return addWaiter(Node.EXCLUSIVE)↓入隊列構造雙向鏈表↓acquireQueued(node, arg)↓while(前驅不是head || 不能獲取鎖)↓ ↓park() tryAcquire()↓獲取成功 → 設置head → unpark下一個
二、AQS 釋放鎖(release)源碼流程
public final boolean release(int arg) {if (tryRelease(arg)) {Node h = head;if (h != null && h.waitStatus != 0)unparkSuccessor(h);return true;}return false;
}
流程圖解:
release()│tryRelease()│是否成功釋放?↓ ↑true false↓獲取 head↓喚醒下一個節點(unpark)
三、ReentrantLock 是怎么用 AQS 實現的?
源碼結構簡圖:
public class ReentrantLock implements Lock {abstract static class Sync extends AbstractQueuedSynchronizer {// 核心邏輯都在此子類中}final Sync sync;
}
ReentrantLock 的兩個實現版本:
類型 | 類名 | 特點 |
---|---|---|
非公平鎖(默認) | NonfairSync | 直接嘗試獲取鎖,搶占式,性能好 |
公平鎖 | FairSync | 排隊獲取,按順序,不插隊,公平但慢一些 |
tryAcquire 的實現(非公平鎖)
protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {// 嘗試直接 CAS 獲取鎖if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}} else if (current == getExclusiveOwnerThread()) {// 可重入,加重入次數int nextc = c + acquires;setState(nextc);return true;}return false;
}
tryRelease 的實現
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;
}
四、AQS 線程狀態控制核心機制:LockSupport
AQS 底層通過 LockSupport.park()
和 unpark(thread)
來掛起/喚醒線程:
park()
:當前線程阻塞,等待被喚醒;unpark(Thread t)
:喚醒指定線程;- 這種控制機制替代了傳統的
wait/notify
,更靈活、底層、性能好。
五、總結:如何理解 AQS 的強大之處?
特性 | 說明 |
---|---|
模板方法設計 | 只需實現 tryAcquire 和 tryRelease ,其他線程隊列處理由 AQS 自動完成 |
雙模式支持 | 支持共享與獨占兩種訪問模型 |
高可復用性 | 可構建多種同步組件(鎖、信號量、柵欄等) |
高性能 | 結合 CAS、自旋、隊列掛起喚醒機制 |
阻塞線程管理 | 使用 LockSupport 精細控制線程掛起與恢復 |