主题
概念卡片:AQS 同步器框架
一句话机制
AQS(AbstractQueuedSynchronizer,抽象队列同步器)是 JUC 的「同步器模板」,用「state 状态变量 + FIFO 等待队列 + CAS 修改 + LockSupport park/unpark」四件套,把「抢锁、排队、阻塞、唤醒」的通用逻辑抽出来,让 ReentrantLock/Semaphore/CountDownLatch 只定义各自的资源语义。 它不是具体的锁,而是构建锁和同步工具的基础模板。
核心组成
AQS = state 状态变量 + FIFO 等待队列 + CAS 修改 + park/unpark 阻塞唤醒| 组成 | 作用 | 细节 |
|---|---|---|
volatile int state | 同步状态 | 含义由具体同步器定义(见下) |
| FIFO 队列 | 管理抢不到资源的线程 | 线程包装成 Node 节点,head <-> node1 <-> ... <-> tail |
| CAS | 保证 state 修改原子 | 抢锁时只有一个线程能把 state 0→1 |
| LockSupport | 阻塞/唤醒 | park() 挂起、unpark(thread) 唤醒 |
state 在不同组件的含义
| 组件 | state 含义 |
|---|---|
| ReentrantLock | 锁占用(0=空闲,>0=占用且数值=重入次数) |
| CountDownLatch | 计数器(countDown() 减 1,归 0 唤醒等待线程) |
| Semaphore | 许可证数量(acquire() 减、release() 加) |
两种模式 + 模板方法
独占模式:同一时刻只有一个线程获资源(ReentrantLock)。 共享模式:多个线程同时获资源(Semaphore、CountDownLatch、读写锁的读锁)。
AQS 用模板方法思想:通用流程(入队/阻塞/唤醒/维护队列)由 AQS 实现,具体同步器只需实现资源语义:
java
protected boolean tryAcquire(int arg); // 独占获取
protected boolean tryRelease(int arg); // 独占释放
protected int tryAcquireShared(int arg); // 共享获取(≥0 成功)
protected boolean tryReleaseShared(int arg); // 共享释放执行流程(CAS + 队列 + 阻塞唤醒)
1. CAS 尝试快速获取资源
2. 失败 → 线程包装成 Node 入队
3. 必要时 park() 阻塞线程(不空转耗 CPU)
4. 资源释放 → unpark() 唤醒后继节点再试不是单纯自旋、也不是单纯阻塞,而是「先 CAS 抢一次 → 抢不到就排队阻塞 → 释放后唤醒」的组合,兼顾了低竞争下的快和高竞争下的省。
公平锁 vs 非公平锁(AQS 排队机制)
new ReentrantLock(false) 默认非公平,new ReentrantLock(true) 公平。
- 非公平锁:新线程来了若锁恰好空闲,可直接抢,不排队 → 吞吐高,但队列线程可能饥饿
- 公平锁:先检查队列是否有人排队,有人就进队尾 → 公平,但吞吐低
常见误解(避坑)
- ❌ "AQS 是一个具体的锁"。→ 它是框架/模板,ReentrantLock/Semaphore/CountDownLatch/ReentrantReadWriteLock 都建立在其上。
- ❌ "AQS 全靠 CAS 自旋"。→ 它是「CAS 抢一次 + 失败入队阻塞 + 释放唤醒」,不是纯自旋(纯自旋会耗 CPU)。
- ❌ "所有同步工具都用独占模式"。→ 还有共享模式(Semaphore/CountDownLatch/读锁),资源释放后可连续唤醒多个等待线程。
- ❌ "state 的值是固定的含义"。→ state 只是一个 int,含义由具体同步器定义(锁占用数/计数器/许可证数),AQS 本身不关心它代表什么。
关联
- 原始资料:AQS详解
- 总览:并发编程技术栈总览
- 相关卡:概念卡片:CAS乐观锁与原子类 · 概念卡片:synchronized锁升级与Monitor · 概念卡片:Java线程池ThreadPoolExecutor
- 域地图:A00-百科/Java后端/Java后端