AQS(抽象队列同步器) 是 Java 并发包的核心基础框架,用于构建锁和同步工具(如 ReentrantLock、Semaphore 等)。它通过 共享状态管理 和 线程排队机制 实现高效的线程同步。以下是其核心要点:
一、AQS 的核心组成
-
状态变量(
state)- 一个
volatile int值,表示同步状态(如锁是否被占用、信号量剩余许可数等)。 - 子类通过操作
state定义同步规则(如ReentrantLock中state=0表示未加锁,state>0表示锁被持有且可重入次数)。
- 一个
-
CLH 队列(线程等待队列)
- 一个双向链表结构的队列,存储等待获取资源的线程。
- 每个节点(
Node)包含线程引用、等待状态(如是否被取消)及前后指针。
二、AQS 的工作原理
-
获取资源(如加锁)
- 线程尝试通过 CAS 修改
state,若成功则获取资源。 - 若失败,将线程包装为
Node加入 CLH 队列尾部,并阻塞(通过LockSupport.park())。
- 线程尝试通过 CAS 修改
-
释放资源(如解锁)
- 修改
state,并唤醒队列中下一个等待的线程(通过LockSupport.unpark())。
- 修改
三、AQS 的两种模式
| 模式 | 特点 | 应用场景 |
|---|---|---|
| 独占模式 | 同一时刻只有一个线程能获取资源(如锁) | ReentrantLock |
| 共享模式 | 多个线程可同时获取资源 | Semaphore、CountDownLatch |
四、AQS 的模板方法
子类需实现以下方法定义同步逻辑:
-
独占模式:
protected boolean tryAcquire(int arg) // 尝试获取资源 protected boolean tryRelease(int arg) // 尝试释放资源 -
共享模式:
protected int tryAcquireShared(int arg) // 尝试获取共享资源 protected boolean tryReleaseShared(int arg) // 尝试释放共享资源
示例:ReentrantLock 的实现
protected boolean tryAcquire(int arg) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) { // 锁未被占用
if (compareAndSetState(0, 1)) { // CAS 修改 state
setExclusiveOwnerThread(current); // 设置当前线程为锁持有者
return true;
}
} else if (current == getExclusiveOwnerThread()) { // 可重入
setState(c + 1); // 增加重入次数
return true;
}
return false;
}
五、AQS 的等待队列管理
-
入队:
- 线程获取资源失败时,创建
Node并入队,通过自旋 CAS 确保线程安全。
- 线程获取资源失败时,创建
-
出队:
- 前驱节点释放资源后,唤醒后续节点尝试获取资源。
六、AQS 的典型应用
| 同步工具 | 实现原理 |
|---|---|
| ReentrantLock | 基于独占模式,state 表示锁的持有计数(可重入)。 |
| Semaphore | 基于共享模式,state 表示剩余许可数,获取许可时减少,释放时增加。 |
| CountDownLatch | 基于共享模式,state 初始为计数,线程调用 countDown() 减1,减到0时唤醒所有等待线程。 |
七、AQS 的优缺点
| 优点 | 缺点 |
|---|---|
| 提供通用同步框架,减少重复造轮子 | 需要理解底层原理才能正确扩展 |
| 高性能(CAS + CLH 队列优化) | 调试复杂(涉及线程阻塞与唤醒) |
八、总结
AQS 是 Java 并发编程的基石,通过状态管理和线程排队机制,为构建高效、灵活的同步工具提供基础支持。
核心口诀:
「AQS 管状态,队列排线程
独占共享两模式,模板方法定规则
ReentrantLock、Semaphore,皆由此来显神通!」