AQS 是什么?state 与同步队列如何协作?
结论先行:AQS(AbstractQueuedSynchronizer)是 JUC 锁与同步器的公共底座:用 volatile int state 表示同步状态,用内置的 CLH 变体双向队列管理抢锁失败的线程。ReentrantLock、Semaphore、CountDownLatch、ReentrantReadWriteLock 都继承它,只需覆写 tryAcquire/tryRelease 等少量钩子方法。
两个核心成员
- state:volatile 修饰的 int。含义由子类自定义——ReentrantLock 中表示可重入次数(0 表示无人持有),Semaphore 中表示剩余许可数,CountDownLatch 中表示待倒数计数。
- 同步队列:双向链表,节点保存线程与等待状态;抢锁失败者包装成节点入队并挂起(LockSupport.park),前驱节点释放后唤醒后继。
- CLH 变体:AQS 队列基于 CLH 改造,节点直接持有线程引用并支持取消,因此能实现超时与中断。
设计精髓:AQS 把排队、挂起、唤醒等通用逻辑固化,子类只需回答“状态够不够、怎么改状态”两个问题。
工作流程速记
- 线程尝试获取:调用子类实现的 tryAcquire,成功直接返回。
- 失败则把线程包装成 Node 以 CAS 方式插入队尾,保证并发入队安全。
- 入队后自旋或阻塞,等待前驱节点释放并发起 unpark 唤醒。
- 释放时先 tryRelease 修改 state,再唤醒队首的后继节点继续竞争。
- 公平差异:非公平锁入队前先 CAS 抢一次,公平锁严格按 FIFO 依次放行。
import java.util.concurrent.locks.ReentrantLock;
public class AqsDemo {
private final ReentrantLock lock = new ReentrantLock(); // 内部就是一个 AQS
public void work() {
lock.lock(); // 等价 acquire(1):期望 state 0→1
try {
// 临界区
} finally {
lock.unlock(); // 等价 release(1):state 1→0 并唤醒后继
}
}
}
常见追问 / 记忆点
- 记忆点:AQS = “一个 state + 一条同步队列”,子类用 tryAcquire/tryRelease 定义语义。
- 追问:非公平锁在 acquire 前会先直接 CAS 抢一次,抢不到才乖乖排队,这就是“插队”来源。
- 追问:可重入靠 state 累加实现——同一线程重复 lock 使 state 递增,unlock 次数匹配后才真正释放。
- 追问:Condition 额外维护一条等待队列,比 Object.wait 支持多条件与超时,是读写锁场景的常客。