aqs通过volatile双向链表实现fifo等待队列,以head和tail维护队首队尾,失败线程封装为node入队尾并阻塞,仅head.next节点可被前驱唤醒;队列懒加载,首节点初始化为哨兵节点,node含thread、prev/next、waitstatus、mode字段;入队用cas保证原子性,失败则自旋重试;唤醒由release触发,检查head.next的waitstatus为signal后unpark;state为volatile核心变量,0为空闲,>0为占用,获取失败先试tryacquire,入队后仍需循环检查state,决策权在子类实现。

AQS 内部通过一个 volatile 修饰的双向链表实现 FIFO 等待队列,核心是用 head 和 tail 两个节点引用维护队首与队尾,所有等待获取同步状态失败的线程都会被封装为 Node 节点加入该队列尾部,并被阻塞;只有队首节点(即 head 的后继节点)有资格被唤醒并重试获取锁。
队列结构与节点组成
AQS 队列是一个懒加载的双向链表,初始时 head 和 tail 均为 null。首次入队会触发初始化:先创建一个空的哨兵节点(dummy node)作为 head,再将首个等待线程构造成 Node 插入其后。每个 Node 包含:
- thread:指向被阻塞的线程引用
- prev / next:分别指向前驱和后继节点,构成双向链接
- waitStatus:标记当前等待状态(如 SIGNAL、CANCELLED、CONDITION 等)
- mode:标识是 EXCLUSIVE(独占)还是 SHARED(共享)模式入队
入队过程保证线程安全
新节点加入队尾不是简单赋值,而是通过 CAS 操作确保原子性:
- 先读取当前 tail 引用,尝试用 compareAndSetTail(oldTail, newNode) 更新尾指针
- 若失败(说明其他线程已抢先更新),则进入自旋重试逻辑(enq 方法)
- 成功后,将原 tail 的 next 指向新节点,新节点 prev 指向原 tail
这种设计避免了锁竞争,也规避了 CAS 失败导致的数据不一致问题。
唤醒机制依赖前驱节点通知
队列中只有 head.next 对应的节点能被唤醒,且唤醒动作由其前驱节点(即 head)在释放资源时主动触发:
- 当持有锁的线程调用 release(),成功更新 state 后,会检查 head.next 的 waitStatus 是否为 SIGNAL
- 若是,则调用 LockSupport.unpark(node.thread) 唤醒该线程
- 被唤醒线程再次尝试 tryAcquire(),成功则成为新 head(原 head 被断开引用,不再关联任何线程)
state 变量与队列协同工作
state 是 AQS 的核心状态变量,volatile 修饰保障可见性,所有修改均基于 CAS:
- state = 0 表示资源空闲(如 ReentrantLock 未被占用)
- state > 0 表示已被占用,具体含义由子类定义(如可重入次数、剩余信号量数等)
- 获取失败时,线程入队前会先尝试一次 tryAcquire;入队后仍需循环检查 state 是否就绪,而非被动等待信号
队列本身不决定“谁该获得资源”,它只提供有序排队和公平唤醒的基础设施,真正决策权在子类对 tryAcquire/tryRelease 的实现里。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











