AQS是同步器模板,核心为state状态、双向等待队列和CAS+park/unpark机制;ReentrantLock是其典型应用,需重点理解state变化、Node入队与唤醒逻辑,避开与阻塞队列等概念混淆。

掌握 AQS 不需要死磕每一行源码,关键在于抓住它的骨架逻辑和设计意图。它不是用来背的,而是用来“看懂系统怎么排队、怎么发号、怎么叫号”的。
先建立三层认知结构
别一上来就翻 acquireQueued 方法。先理清 AQS 是什么角色:
- 它是个“同步器模板”:不是具体锁,而是 ReentrantLock、Semaphore、CountDownLatch 这些工具的公共底盘;
-
它管三件事:一个
volatile int state(门禁状态)、一个双向等待队列(排队区)、一套 CAS + park/unpark(叫号与候场机制); -
它把“能不能抢到”留给子类决定:比如
tryAcquire是 ReentrantLock 自己写的逻辑,AQS 只负责“抢失败了就排队,释放了就叫下一位”。
用 ReentrantLock 当主线来学最有效
这是 AQS 最典型、最干净的独占模式应用。建议按这个顺序走一遍:
- 写一段带公平/非公平参数的
ReentrantLock lock = new ReentrantLock(true)代码,加锁、释放、重入,观察行为差异; - 调试进入
lock.lock()→sync.acquire(1)→tryAcquire(1),只看这三步里 state 怎么变、head/tail 怎么初始化、Node 怎么入队; - 重点盯两个值:
state(0/1/2 表示无锁/持有一把/重入两次)、waitStatus(-1 表示“我后面有人,轮到我得通知他”)。
画图比读代码更快理解队列流转
AQS 的队列不是 ArrayList,是靠 Node.prev/Node.next 链起来的虚拟双向链表。动手画三次:
- 线程 A 成功获取锁:head 指向 dummy node,state=1;
- 线程 B 抢锁失败:封装为 Node 插入 tail,tail 移动,B 调用
park()挂起; - 线程 A 释放锁:state=0,唤醒 B(
unpark(B)),B 唤醒后检查前驱是否为 head,再尝试 CAS 抢锁。
避开三个常见误区
很多卡点其实源于误解:
- 别混淆 AQS 和阻塞队列(BlockingQueue):前者队列里存的是“等锁的线程”,后者存的是“待处理的任务”;
-
别以为 Condition 是 AQS 的一部分:它是 AQS 内部的嵌套类
ConditionObject,用的是另一条单向条件队列,和主 CLH 队列分开管理; - 别执着于“必须看懂所有 waitStatus 取值”:日常开发真正高频用到的只有 0、-1(SIGNAL)、1(CANCELLED),先吃透这三个就够了。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











