AQS(AbstractQueuedSynchronizer):Java 同步框架的基石
AQS(AbstractQueuedSynchronizer,队列同步器)是 Java 并发包(JUC)的核心基础组件,为 ReentrantLock、Semaphore、CountDownLatch 等同步工具提供统一的底层支持。它通过同步状态管理和FIFO 等待队列实现了高效的线程同步机制。本文将深入解析 AQS 的设计原理、核心组件及工作流程。
AQS 的核心设计思想
AQS 的核心目标是简化同步组件的实现,其设计基于模板方法模式:
- 子类通过继承 AQS 并实现特定抽象方法(如
tryAcquire
、tryRelease
)来定义同步状态的获取与释放规则; - AQS 自身实现了队列管理、线程阻塞 / 唤醒等通用逻辑,无需子类重复开发。
核心组件
- 同步状态(state):一个
volatile int
变量,用于表示资源的占有状态(如 0 表示未占用,1 表示已占用); - FIFO 等待队列:双向链表结构,存储等待获取资源的线程节点;
- 条件队列(Condition):与
Lock
配合使用,实现线程间的条件等待。
AQS 核心源码解析
同步状态(state)
1 | public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements Serializable { |
状态的意义:
- 对于
ReentrantLock
,state
表示重入次数(0:未锁定;n>0:被锁定 n 次); - 对于
Semaphore
,state
表示剩余许可数; - 对于
CountDownLatch
,state
表示计数器值。
FIFO 等待队列(同步队列)
等待队列是 AQS 的核心,用于存储因获取资源失败而阻塞的线程。队列由Node
节点组成,结构如下:
Node 节点结构
1 | static final class Node { |
队列结构
1 | head -> Node1 -> Node2 -> ... -> tail |
head
:头节点(哨兵节点,无实际线程);tail
:尾节点(最新加入的节点);- 节点通过
prev
和next
形成双向链表。
独占模式下的资源获取与释放
以ReentrantLock
的独占锁为例,解析 AQS 的核心流程。
获取资源:acquire(int arg)
1 | public final void acquire(int arg) { |
tryAcquire
:子类实现,定义独占模式下的资源获取逻辑(如ReentrantLock
判断state
是否为 0,是则 CAS 设置为 1)。addWaiter
:将当前线程封装为 Node,通过 CAS 加入队列尾部:1
2
3
4
5
6
7
8
9
10
11
12
13private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node); // 队列为空或CAS失败时,自旋入队
return node;
}acquireQueued
:节点在队列中自旋等待,直到获取资源或被取消:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22final 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; // 帮助GC
failed = false;
return interrupted;
}
// 判断是否可以阻塞,若可以则park当前线程
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) {
interrupted = true;
}
}
} finally {
if (failed) cancelAcquire(node); // 获取失败,取消节点
}
}
释放资源:release(int arg)
1 | public final boolean release(int arg) { |
tryRelease
:子类实现,定义资源释放逻辑(如ReentrantLock
将state
减 1,为 0 时表示完全释放)。unparkSuccessor
:唤醒后继节点(移除取消节点,调用LockSupport.unpark
)。
条件队列(Condition)
AQS 内部类ConditionObject
实现了Condition
接口,与Lock
配合实现线程间的条件等待(类似synchronized
的wait/notify
)。
核心方法
await()
:将当前线程加入条件队列,释放锁并阻塞;signal()
:将条件队列的首节点转移到同步队列,等待获取锁;signalAll()
:转移所有条件队列节点到同步队列。
工作流程
- 线程获取锁后,调用
await()
进入条件队列并释放锁; - 其他线程调用
signal()
,将条件队列节点转移到同步队列; - 同步队列中的节点竞争锁,获取后继续执行。
LockSupport 工具类
AQS 通过LockSupport
实现线程的阻塞与唤醒,其核心方法:
park()
:阻塞当前线程(需unpark
唤醒);unpark(Thread t)
:唤醒指定线程(提前调用可设置 “许可”,使后续park
不阻塞)。
LockSupport
相比wait/notify
的优势:
- 无需在同步块中调用;
- 支持中断响应;
- 避免虚假唤醒(通过 “许可” 机制)。
AQS 的应用:同步组件举例
ReentrantLock(独占锁)
tryAcquire
:判断state
是否为 0,是则 CAS 设置为 1(非公平锁);或检查是否为当前线程,是则state++
(重入)。tryRelease
:state--
,为 0 时释放锁并返回true
。
Semaphore(信号量)
- 共享模式:多个线程可同时获取许可。
tryAcquireShared
:state
减去请求的许可数,若 >=0 则成功。tryReleaseShared
:state
加上释放的许可数,唤醒后续节点。
CountDownLatch(倒计时器)
tryAcquireShared
:若state
为 0 则成功,否则失败并加入队列。countDown()
:调用tryReleaseShared
,state--
,为 0 时唤醒所有等待线程。
AQS 的核心优势
- 通用性:统一实现队列管理、线程阻塞等逻辑,简化同步组件开发;
- 高效性:基于 CAS 和自旋锁,减少锁竞争带来的性能损耗;
- 灵活性:支持独占 / 共享模式,可扩展出多种同步工具;
- 安全性:通过 volatile 和 CAS 保证状态的线程可见性与原子性。