1. 基本定义
AbstractQueuedSynchronizer(AQS) 是 Java 并发包(java.util.concurrent.locks
)中的核心框架,用于 构建锁和同步器(如 ReentrantLock
、Semaphore
、CountDownLatch
)。它通过 FIFO 等待队列 管理线程的阻塞与唤醒,并基于 原子状态变量 实现高效的同步控制。
2. 核心组件
(1) 同步状态(state)
- 类型:
volatile int
,表示资源的可用数量或锁的持有状态。 - 操作方法:
protected final int getState(); protected final void setState(int newState); protected final boolean compareAndSetState(int expect, int update); // CAS 操作
- 作用:通过原子操作管理资源状态(如
ReentrantLock
中state=1
表示锁被占用)。
(2) 等待队列(CLH 队列)
- 结构:双向链表,每个节点(
Node
)保存等待线程及状态(如是否被取消、是否需要唤醒后续节点)。 - 节点状态:
CANCELLED (1)
:线程已放弃等待(如超时或中断)。SIGNAL (-1)
:当前节点的后续节点需要被唤醒。CONDITION (-2)
:节点处于条件队列(如Condition
的等待队列)。
(3) 模式
- 独占模式(Exclusive):同一时间仅一个线程可获取资源(如
ReentrantLock
)。 - 共享模式(Shared):多个线程可同时获取资源(如
Semaphore
)。
3. 关键方法
AQS 提供 模板方法,需子类实现 核心钩子方法 定义同步逻辑:
方法 | 描述 |
---|---|
tryAcquire(int arg) | 独占模式下尝试获取资源(需子类实现)。 |
tryRelease(int arg) | 独占模式下尝试释放资源(需子类实现)。 |
tryAcquireShared(int arg) | 共享模式下尝试获取资源(需子类实现)。 |
tryReleaseShared(int arg) | 共享模式下尝试释放资源(需子类实现)。 |
isHeldExclusively() | 当前线程是否独占资源(用于条件变量实现)。 |
4. 工作流程(以独占模式为例)
(1) 获取资源(acquire)
- 调用子类的
tryAcquire()
尝试直接获取资源。 - 若失败,将线程封装为节点加入等待队列尾部,并通过
LockSupport.park()
阻塞线程。 - 队列中前驱节点释放资源后,唤醒当前节点线程重新尝试获取资源。
(2) 释放资源(release)
- 调用子类的
tryRelease()
释放资源。 - 唤醒队列中的下一个有效节点(
unparkSuccessor()
)。
5. 应用示例:实现独占锁
public class SimpleLock extends AbstractQueuedSynchronizer {// 尝试获取锁(state=0 表示未占用,1 表示已占用)@Overrideprotected boolean tryAcquire(int arg) {return compareAndSetState(0, 1); // CAS 设置状态为 1}// 释放锁@Overrideprotected boolean tryRelease(int arg) {setState(0); // 无需 CAS,只有持有锁的线程能调用 releasereturn true;}// 加锁public void lock() {acquire(1);}// 解锁public void unlock() {release(1);}
}// 使用示例
SimpleLock lock = new SimpleLock();
lock.lock();
try {// 临界区代码
} finally {lock.unlock();
}
6. 核心优势
- 灵活性:通过继承 AQS 并重写钩子方法,可快速实现自定义同步器(如读写锁、信号量)。
- 高效性:基于 CAS 和 CLH 队列,减少线程上下文切换开销。
- 可扩展性:支持独占和共享模式,满足不同并发场景需求。
7. 典型应用
同步工具 | 实现原理 |
---|---|
ReentrantLock | 基于 AQS 独占模式,支持可重入(state 记录重入次数)。 |
Semaphore | 基于 AQS 共享模式,state 表示可用许可数量。 |
CountDownLatch | 基于 AQS 共享模式,state 表示剩余计数,计数为 0 时唤醒所有等待线程。 |
ReentrantReadWriteLock | 读锁(共享模式)和写锁(独占模式)分离,共用同一个 AQS 实例。 |
8. 注意事项
- 避免死锁:确保资源释放逻辑正确(如
finally
块中释放锁)。 - 性能调优:合理设计
tryAcquire
/tryRelease
逻辑,减少 CAS 竞争。 - 公平性选择:AQS 默认非公平锁(允许插队),可通过队列顺序实现公平锁。
总结
AQS 是 Java 并发编程的基石,通过 状态管理 和 等待队列 实现了高效的线程同步机制。开发者通过继承 AQS 并实现核心钩子方法,可快速构建高性能的锁和同步工具(如 ReentrantLock
和 Semaphore
)。深入理解 AQS 的 CLH 队列、CAS 操作 及 模板方法模式,是掌握 Java 并发编程的关键。