aqs底层由volatile int state和双向同步队列构成,state由子类定义语义并用cas操作,同步队列以head/tail node实现clh变体,通过模板方法acquire/release封装排队与唤醒逻辑。

Java 中 AQS 的底层架构,核心就两根支柱:一个 volatile int state 和一条 双向链表构成的同步队列。它不提供具体业务逻辑,而是把“排队、阻塞、唤醒、状态流转”这些通用动作封装好,让 ReentrantLock、Semaphore 等工具只专注定义“资源怎么算被占用”——也就是 state 的含义。
state:统一的资源状态抽象
state 是 AQS 的灵魂变量,用 volatile 修饰,保证多线程下的可见性。它的值本身没有固定语义,全由子类解释:
- 在 ReentrantLock 中,state 表示当前锁的重入次数(0=无锁,1=已上锁,2=重入一次)
- 在 Semaphore 中,state 表示剩余可用许可证数量
- 在 CountDownLatch 中,state 表示还需 await 的倒计数值
所有对 state 的修改都通过 CAS(compareAndSetState)完成,确保并发安全。AQS 提供 getState()、setState()、compareAndSetState() 三个基础操作,子类不能绕过它们直接读写 state。
同步队列:CLH 变体的双向等待链表
AQS 并不使用 Java 集合类实现队列,而是维护 head 和 tail 两个 volatile Node 引用,构建出一个虚拟的双向链表。每个等待线程都被封装为一个 Node 节点,节点含关键字段:
- thread:指向等待的线程
- waitStatus:节点状态(如 SIGNAL=-1 表示后继需唤醒,CANCELLED=1 表示线程已中断或超时取消)
- prev / next:前后节点指针,构成双向链表
线程获取锁失败时,会调用 addWaiter() 将自己构造成 Node,并用 CAS 原子地插入队列尾部;释放锁后,AQS 从 head 开始找第一个 waitStatus 合适的后继节点,用 LockSupport.unpark() 唤醒它。
模板方法:子类只需实现资源语义判断
AQS 是典型的模板方法模式。它把加锁流程固化为 acquire() 方法:
- 先调 tryAcquire() —— 子类实现,决定“这次能不能拿走资源”
- 拿不到就 addWaiter() 入队 + acquireQueued() 自旋等待
- 自旋中只在前驱是 head 时才再次尝试 tryAcquire()
同理,release() 流程也由 tryRelease() + unparkSuccessor() 组成。子类真正要写的,往往只是几行关于 state 比较和更新的逻辑,其余调度全部交给 AQS。
公平性差异藏在 tryAcquire 的第一道门里
公平锁与非公平锁的区别,不在队列结构,而在于 tryAcquire() 的实现时机:
- 非公平锁:一上来就直接 CAS 尝试抢锁(可能插队),失败再进队列
- 公平锁:先查队列是否为空或自己是不是队首,只有满足才去 CAS 抢锁,否则直接入队
也就是说,“是否排队”这个决策发生在获取锁的最前端,而不是在队列操作环节。这也是为什么非公平锁吞吐更高——它给了新线程一次“免排队直通”的机会。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











