AQS核心架构围绕volatile int state同步状态和CLH双向队列展开:state表示资源状态(如锁重入次数、许可数、倒计数值),由volatile保证可见性、CAS保证原子性;CLH队列以Node节点构成虚拟双向链表,管理等待线程,通过waitStatus标识节点状态,head指向哨兵或持有者,tail支持CAS入队,释放锁时唤醒SIGNAL状态后继节点。

AQS 的核心架构围绕两个不可分割的主干展开:一个用 volatile int state 表达的同步状态,和一个基于 CLH 思想改造而来的双向同步队列。它们不独立工作,而是通过 CAS 原子操作与线程调度紧密耦合,共同完成“资源竞争—排队—唤醒”的闭环。
state 变量:同步状态的唯一事实源
state 是 AQS 内部唯一的共享状态变量,所有锁或同步工具的语义都映射到它上面:
- ReentrantLock 中,state = 0 表示无锁,state = 1 表示首次加锁,>1 表示重入次数;
- Semaphore 中,state 表示剩余许可数,每次 acquire() 尝试将 state 减 1(CAS 成功即获取许可);
- CountDownLatch 中,state 是倒计数值,await() 阻塞直到 state 变为 0。
它被 volatile 修饰,确保多线程间可见性;所有修改必须通过 compareAndSetState() 等 CAS 方法,避免锁竞争,实现真正无锁化更新。子类不能直接赋值,只能通过钩子方法(如 tryAcquire)间接控制它的变化逻辑。
双向 CLH 变体队列:等待线程的有序容器
AQS 并没有使用现成的 Queue 实现,而是维护了一个虚拟的、由 Node 节点组成的双向链表——这是对原始 CLH 单向队列的关键升级:
- 每个 Node 包含 thread(关联线程)、prev/next(前后指针)、waitStatus(如 SIGNAL/CANCELLED/CONDITION 等状态码);
- 线程抢锁失败时,会被封装为 Node,以 CAS 方式插入队列尾部(enq 方法);
- head 指向当前持有锁或刚释放锁的节点(通常为空置哨兵节点),真正的等待从 head.next 开始;
- 释放锁后,AQS 会检查 head.next 是否处于 SIGNAL 状态,再调用 LockSupport.unpark() 唤醒对应线程。
双向结构让出队、取消、超时检测等操作更高效——比如 cancel 掉某个节点时,能快速修正其前驱的 next 和后继的 prev,而单向链表做不到这点。
state 与队列如何协同工作
两者的协作不是先后关系,而是一体两面:
- 获取资源时,先尝试 CAS 修改 state;失败则立即入队,并自旋检查前驱节点是否为 head —— 若是,再次尝试获取;否则 park 阻塞;
- 释放资源时,先更新 state,再唤醒队列中第一个有效等待节点(非 CANCELLED);该节点被唤醒后,会重新尝试 CAS 获取 state;
- 公平锁模式下,入队是强制流程(即使 state 刚释放也禁止插队);非公平锁则允许新线程直接 CAS 抢 state,抢不到才排队。
这种设计把“状态判断”和“线程调度”解耦:state 决定能否通行,队列决定谁在等、谁先走,两者配合实现了高并发下既安全又灵活的同步控制。










