AbstractQueuedSynchronizer (AQS) 深度解析
一、是什么?
AbstractQueuedSynchronizer(AQS)是 Java 并发包(java.util.concurrent.locks
)中的核心同步框架,通过内置的 FIFO 队列管理线程阻塞/唤醒,提供了一套模板方法供开发者实现自定义同步器(如锁、信号量)。JDK 中的 ReentrantLock
、Semaphore
等均基于 AQS 实现。
二、解决什么问题
- 同步原语统一管理:避免开发者重复实现线程排队、阻塞/唤醒等底层逻辑
- 性能优化:通过 CAS + 自旋减少线程上下文切换
- 灵活性:支持独占锁(如
ReentrantLock
)和共享锁(如CountDownLatch
) - 解决 synchronized 缺陷:提供可中断锁、超时锁、公平锁等高级特性
AQS 核心实现机制
1. 同步状态(State)
java
private volatile int state; // volatile 保证可见性
- 整型变量,表示资源状态(如锁的重入次数、信号量的许可证数量)
- 通过 CAS 原子操作修改:java
protected final boolean compareAndSetState(int expect, int update) { return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
2. CLH 队列(线程等待队列)
java
// 队列节点(双向链表)
static final class Node {
volatile int waitStatus; // 等待状态(CANCELLED/SIGNAL/CONDITION/PROPAGATE)
volatile Thread thread; // 绑定的线程
volatile Node prev;
volatile Node next;
}
private transient volatile Node head; // 队头
private transient volatile Node tail; // 队尾
- FIFO 队列管理阻塞线程
- 自旋检查:线程入队前短暂自旋尝试获取锁,避免直接阻塞
- 状态标识:
CANCELLED(1)
:线程已取消SIGNAL(-1)
:后继节点需要被唤醒CONDITION(-2)
:线程在条件队列等待
3. 模板方法(开发者需重写)
方法 | 作用 |
---|---|
tryAcquire(int arg) | 尝试获取独占锁 |
tryRelease(int arg) | 尝试释放独占锁 |
tryAcquireShared(int arg) | 尝试获取共享锁(返回剩余数量) |
tryReleaseShared(int arg) | 尝试释放共享锁 |
isHeldExclusively() | 检查是否被当前线程独占 |
关键流程实现
独占锁获取(acquire())
java
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 步骤1:尝试快速获取
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 步骤2-4
selfInterrupt();
}
- tryAcquire():子类实现的快速获取逻辑
- addWaiter():将线程包装为 Node 加入队尾
- acquireQueued():
- 自旋检查前驱节点是否为 head(公平锁核心)
- 通过
LockSupport.park()
阻塞线程
- 被唤醒后检查中断状态
锁释放(release())
java
public final boolean release(int arg) {
if (tryRelease(arg)) { // 子类实现释放逻辑
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); // 唤醒后继节点
return true;
}
return false;
}
unparkSuccessor()
从队尾向前遍历找到最前未取消节点- 通过
LockSupport.unpark(node.thread)
唤醒线程
应用场景
- 独占锁:
ReentrantLock
(可重入锁) - 共享锁:
Semaphore
(信号量控制并发数)CountDownLatch
(倒计数器)CyclicBarrier
(循环屏障)
- 读写锁:
ReentrantReadWriteLock
- 自定义同步器:如数据库连接池限流
Java 示例:自定义互斥锁
java
public class MutexLock extends AbstractQueuedSynchronizer {
// 尝试获取锁
protected boolean tryAcquire(int acquires) {
if (compareAndSetState(0, 1)) { // CAS 修改 state
setExclusiveOwnerThread(Thread.currentThread()); // 设置独占线程
return true;
}
return false;
}
// 尝试释放锁
protected boolean tryRelease(int releases) {
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0); // 注意:state 修改在 owner 之后(保证可见性)
return true;
}
// 加锁入口
public void lock() {
acquire(1);
}
// 解锁入口
public void unlock() {
release(1);
}
}
// 使用示例
MutexLock lock = new MutexLock();
lock.lock();
try {
// 临界区代码
} finally {
lock.unlock();
}
重要注意事项
state 设计:
- 需明确 state 语义(如 ReentrantLock 中:0=未锁定,>0=重入次数)
- 修改 state 必须用 CAS 保证原子性
避免阻塞主线程:
tryAcquire()
中不要调用可能阻塞的操作公平性实现:
java// 公平锁需检查是否有前驱节点 protected final boolean tryAcquire(int acquires) { if (getState() == 0 && !hasQueuedPredecessors() && // 关键公平性检查 compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } // ... 重入逻辑 }
性能优化:
- 在
tryAcquire()
中使用快速路径(如读锁的ThreadLocal
计数) - 避免不必要的唤醒(共享锁的
PROPAGATE
状态)
- 在
AQS 与 synchronized 对比
特性 | AQS | synchronized |
---|---|---|
实现方式 | Java + CAS + 队列 | JVM 内置(C++实现) |
锁类型 | 支持独占/共享两种模式 | 仅独占模式 |
公平性 | 可配置公平/非公平 | 仅非公平 |
锁中断 | 支持 lockInterruptibly() | 不支持 |
超时机制 | 支持 tryLock(timeout) | 不支持 |
条件变量 | 支持多个 Condition | 仅一个 wait/notify |
性能 | 低竞争场景更优 | 高竞争场景优化更好(JDK6+) |
总结
- AQS 是 JUC 并发包的基石,通过模板方法模式分离通用逻辑(队列管理)与自定义逻辑(资源获取)
- 核心机制:
- volatile state + CLH 队列 + CAS 操作
- 线程阻塞/唤醒通过
LockSupport
实现
- 最佳实践:
- 优先使用 JUC 内置同步器(如
ReentrantLock
) - 自定义同步器时明确资源访问模型(独占/共享)
- 高并发场景测试公平/非公平锁的性能差异
- 优先使用 JUC 内置同步器(如
- 演进:JDK 15 引入
VarHandle
替代Unsafe
操作,未来版本可能进一步优化
理解 AQS 需要结合源码分析(推荐阅读
ReentrantLock
实现),掌握其"快速尝试 → 入队 → 阻塞 → 唤醒"的工作流,这是构建高性能并发工具的关键。