AQS 是 Java 并发包中构建锁和同步器的核心框架,其通过 双向队列管理线程等待状态 和 共享资源状态(state) 的原子操作,实现高效的线程同步。以下是其实现原理的分步解析:
1. 核心数据结构与状态管理
- state 变量
由volatile
修饰的整型变量,表示共享资源的状态,如锁的重入次数(ReentrantLock)、信号量许可数(Semaphore)。
arduino
private volatile int state; // 核心状态变量
同步队列(CLH 变种)
双向链表结构,节点为 Node
类型,保存等待线程及状态:
arduino
static final class Node {
volatile int waitStatus; // 节点状态(CANCELLED/SIGNAL/CONDITION/PROPAGATE)
volatile Node prev; // 前驱节点
volatile Node next; // 后继节点
volatile Thread thread; // 绑定的线程
Node nextWaiter; // 条件队列或共享模式标记
}
2. 资源获取与释放流程
2.1 独占模式(如 ReentrantLock)
- 获取资源(acquire)
scss
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 子类实现:尝试直接获取资源
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 失败则入队并阻塞
selfInterrupt(); // 恢复中断状态
}
- tryAcquire:子类自定义资源获取逻辑(如 CAS 修改 state)。
- addWaiter :将当前线程封装为
Node.EXCLUSIVE
节点,通过 CAS 加入队列尾部。 - acquireQueued:自旋或阻塞等待,直到被前驱节点唤醒并成功获取资源。
释放资源(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;
}
- tryRelease:子类定义资源释放逻辑(如减少 state)。
- unparkSuccessor:唤醒队列中第一个有效节点的线程。
2.2 共享模式(如 Semaphore)
-
获取资源(acquireShared)
arduinopublic final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0) // 子类实现:尝试获取共享资源 doAcquireShared(arg); // 入队并等待 }
- tryAcquireShared:返回剩余可用资源数,负值表示失败。
- doAcquireShared:类似独占模式,但成功获取后会传播唤醒后续共享节点。
-
释放资源(releaseShared)
arduinopublic final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { // 子类实现:释放资源 doReleaseShared(); // 唤醒后续节点并传播 return true; } return false; }
3. 同步队列操作细节
- 入队(addWaiter)
通过 CAS 自旋确保线程安全地插入尾节点:
ini
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) { // CAS 更新尾节点
pred.next = node;
return node;
}
}
enq(node); // 队列为空或 CAS 失败时,循环入队
return node;
}
阻塞与唤醒(LockSupport)
- 挂起线程 :
LockSupport.park(this)
在acquireQueued
中调用。 - 唤醒线程 :
LockSupport.unpark(thread)
在unparkSuccessor
中触发
4. 条件变量(ConditionObject)实现
- 条件队列
单向链表维护等待条件的线程,节点通过nextWaiter
链接。
java
public class ConditionObject implements Condition {
private transient Node firstWaiter; // 条件队列头
private transient Node lastWaiter; // 条件队列尾
}
-
await() 流程
- 释放当前线程持有的锁(修改 state)。
- 将线程加入条件队列。
- 阻塞直到被 signal() 或中断。
- 重新竞争锁。
-
signal() 流程
- 将条件队列中的节点转移到同步队列。
- 修改节点状态为
SIGNAL
,等待被唤醒。
5. 关键状态与 CAS 操作
-
waitStatus 状态
- CANCELLED (1) :节点因超时或中断被取消。
- SIGNAL (-1) :后继节点需要被唤醒。
- CONDITION (-2) :节点在条件队列中。
- PROPAGATE (-3) :共享模式下唤醒需传播。
kotlin
// 更新尾节点
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
6. 公平与非公平锁实现
-
非公平锁
线程直接尝试获取锁(插队),减少上下文切换:
arduinofinal boolean nonfairTryAcquire(int acquires) { // 直接尝试 CAS 修改 state,不检查队列 }
-
公平锁
检查同步队列是否有等待节点,避免插队:
arduinoprotected final boolean tryAcquire(int acquires) { if (hasQueuedPredecessors()) // 检查队列是否为空 return false; // CAS 修改 state }
7. 总结:AQS 核心设计优势
设计要点 | 实现方式 | 优势 |
---|---|---|
状态管理 | volatile state + CAS 原子操作 |
高效、无锁的并发控制 |
队列调度 | 双向同步队列 + 条件队列 | 灵活支持独占/共享模式及条件变量 |
模板方法模式 | 子类实现 tryAcquire /tryRelease |
高度可扩展性 |
线程阻塞与唤醒 | LockSupport.park() /unpark() |
精准控制线程状态,避免忙等待 |
通过上述机制,AQS 成为 Java 并发包中高效同步器(如 ReentrantLock
、CountDownLatch
、Semaphore
)的基石,其设计平衡了性能、灵活性和可扩展性。