AQS 是 Java 并发包 (java.util.concurrent.locks
) 的核心基础框架,它为实现各种同步器提供了可重用的基础设施。
1. AQS 核心设计思想
AQS 采用 模板方法模式,将同步器的实现分为两部分:
- 共享资源状态管理:通过一个 volatile int 类型的
state
表示 - 线程排队机制:采用 CLH 变体的 FIFO 双向队列
1.1 核心数据结构
// 同步队列节点
static final class Node {
volatile int waitStatus; // 等待状态
volatile Node prev; // 前驱节点
volatile Node next; // 后继节点
volatile Thread thread; // 关联线程
Node nextWaiter; // 条件队列链接
}
// 关键字段
private transient volatile Node head; // 队首
private transient volatile Node tail; // 队尾
private volatile int state; // 同步状态
2. 同步状态管理
2.1 状态访问方法
// 获取状态
protected final int getState() {
return state;
}
// 设置状态
protected final void setState(int newState) {
state = newState;
}
// CAS 更新状态
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
2.2 状态语义
-
state
的具体语义由子类定义,例如:ReentrantLock
:表示持有锁的计数Semaphore
:表示可用许可数CountDownLatch
:表示剩余计数
3. 同步队列工作原理
3.1 入队流程(获取资源失败时)
- 创建节点并入队(CAS 设置 tail)
- 自旋检查前驱节点状态
- 必要时挂起线程(通过
LockSupport.park()
)
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; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
3.2 出队流程(释放资源时)
- 更新 state 状态
- 唤醒后继节点(通过
LockSupport.unpark()
)
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);
}
4. 关键模板方法
AQS 定义以下需要子类实现的方法:
方法名 | 作用 |
---|---|
tryAcquire(int) | 尝试获取独占锁 |
tryRelease(int) | 尝试释放独占锁 |
tryAcquireShared(int) | 尝试获取共享锁 |
tryReleaseShared(int) | 尝试释放共享锁 |
isHeldExclusively() | 判断是否被当前线程独占 |
5. 两种同步模式
5.1 独占模式 (Exclusive)
- 同一时刻只有一个线程能获取资源
- 实现类:
ReentrantLock
// 获取锁示例
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
5.2 共享模式 (Shared)
- 多个线程可以同时获取资源
- 实现类:
Semaphore
,CountDownLatch
// 获取共享锁示例
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
6. 条件变量支持
AQS 通过 ConditionObject
实现条件等待机制:
public class ConditionObject implements Condition {
private transient Node firstWaiter; // 条件队列头
private transient Node lastWaiter; // 条件队列尾
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
// ...
}
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
}
7. AQS 在 JUC 中的应用
同步器 | AQS 使用方式 |
---|---|
ReentrantLock | 独占模式,state 表示重入次数 |
ReentrantReadWriteLock | 高16位表示读锁,低16位表示写锁 |
Semaphore | 共享模式,state 表示可用许可数 |
CountDownLatch | 共享模式,state 表示剩余计数 |
FutureTask | 表示任务状态 |
8. 关键设计优势
-
性能优化:
- 自旋尝试获取锁减少线程挂起
- 通过 CAS 避免锁竞争
- 延迟初始化队列头节点
-
灵活性:
- 支持公平/非公平策略
- 同时支持独占和共享模式
- 可扩展条件变量
-
可靠性:
- 正确处理线程中断
- 避免死锁设计
- 完善的取消机制