aqs核心是通过volatile int state和双向链表构成的同步队列(fifo,head/tail管理)协调线程等待与唤醒,node封装线程及状态(如signal、cancelled),条件队列则为单向链表且仅配合condition使用。

分析 JDK 源码中 AbstractQueuedSynchronizer(AQS) 的队列,核心是理解它如何用双向链表组织线程、管理等待与唤醒,并通过 state 和 Node 协同完成同步控制。它不是通用容器,而是为锁和同步工具定制的轻量级调度骨架。
看清楚两个关键队列:同步队列 vs 条件队列
AQS 内部维护两类队列,但只有同步队列(Sync Queue)是必需且始终存在的:
-
同步队列:双向链表,由
head和tail指针维护,所有竞争资源失败的线程都会被封装成Node加入队尾;头节点代表当前持有资源的线程(或空闲状态),后继节点在前驱释放资源后被唤醒。 -
条件队列(Condition Queue):单向链表,仅当使用
Condition(如ReentrantLock.newCondition())时才创建,每个Condition对象维护独立队列;节点进入条件队列前会从同步队列移除,满足条件后才重新入同步队列排队。
重点追踪 Node 类的结构和状态流转
Node 是队列的单元,不是简单容器,而是承载调度语义的状态机:
- 关键字段:
thread(关联线程)、prev/next(双向链接)、waitStatus(状态标识)、nextWaiter(区分共享/独占模式,或指向条件队列下一个节点)。 - 常用状态值:
0(初始)、SIGNAL(-1)(需被前驱唤醒)、CANCELLED(1)(因中断/超时失效,后续会被跳过)、CONDITION(-2)(在条件队列中)。 - 注意:
waitStatus只能通过 CAS 原子更新,且一旦设为CANCELLED就不再变更,清理靠前驱节点在唤醒时跳过它实现。
顺着典型流程读源码:入队、阻塞、唤醒
不要从头读类定义,而是抓三条主线走一遍真实路径:
-
入队(addWaiter):线程获取资源失败 → 封装为
Node→ CAS 尝试插入队尾;失败则退化为循环 + CAS,确保最终入队成功;插入后会尝试设置前驱节点的SIGNAL状态。 -
阻塞(acquireQueued):节点入队后进入自旋 → 检查是否为 head.next → 是则再次 tryAcquire;否则调用
LockSupport.park()挂起;挂起前确保前驱状态为SIGNAL,否则重试。 -
唤醒(unparkSuccessor):释放资源时,从 head 开始找第一个
waitStatus ≤ 0的有效后继 → 调用LockSupport.unpark();被唤醒线程回到 acquireQueued 自旋逻辑中继续竞争。
结合 state 字段理解资源抽象能力
state 是 AQS 的灵魂变量,它本身不表示“锁”,而是由子类赋予语义:
-
ReentrantLock中,state = 0表示无锁,state > 0表示重入次数; -
Semaphore中,state表示剩余许可数; -
CountDownLatch中,state表示倒计数值。 - 所有对
state的读写都基于Unsafe.compareAndSwapInt,保证原子性;子类必须实现tryAcquire/tryRelease等模板方法,把业务逻辑映射到state变更上。











