aqs底层队列不区分锁类型,统一为双向fifo链表;独占与共享语义由acquire/acquireshared等方法中state判断逻辑和唤醒策略决定:独占只唤醒一个exclusive后继,共享可传播唤醒多个连续shared节点。

Java中AQS的底层队列本身**不区分独占或共享锁类型**,它是一个统一的双向FIFO等待队列(CLH变种),真正决定“独占”还是“共享”行为的是acquire / acquireShared等模板方法调用时所使用的**同步状态判断逻辑**和**节点唤醒策略**。
队列结构:统一的Node双向链表
AQS内部维护一个volatile Node head和volatile Node tail,所有阻塞线程都被封装为Node节点加入该队列。每个Node包含:
- prev / next:指向前后节点,构成双向链表
- thread:关联被挂起的线程
-
waitStatus:标记节点状态(如
CANCELLED、SIGNAL、CONDITION等) -
nextWaiter:在Condition队列中复用;在同步队列中,对共享模式节点设为
SHARED,对独占模式节点设为EXCLUSIVE(仅作标识,不参与队列组织)
独占锁的排队与唤醒机制
调用acquire(int arg)时走独占流程:
- 先尝试
tryAcquire(arg)——由子类实现,通常基于state值做CAS争抢(如ReentrantLock中state=0表示空闲) - 失败则构造
EXCLUSIVE节点入队,并调用acquireQueued -
acquireQueued中:当前节点前驱若为head,再次尝试tryAcquire;否则将前驱waitStatus设为SIGNAL后挂起自己 - 释放锁时(
release),调用unparkSuccessor(head),**只唤醒头节点的直接后继**(且该后继必须是waitStatus非CANCELLED的EXCLUSIVE节点)
共享锁的排队与唤醒机制
调用acquireShared(int arg)时走共享流程:
- 先尝试
tryAcquireShared(arg)——返回负数表示失败,0表示成功但不传播,正数表示成功且可向后继传播 - 失败则构造
SHARED节点入队(nextWaiter = SHARED),进入doAcquireShared - 唤醒逻辑不同:
setHeadAndPropagate在成功获取后,会检查后继节点是否为SHARED,若是且tryAcquireShared仍能成功,就继续唤醒它——形成“传播式唤醒” - 释放时(
releaseShared)调用doReleaseShared,会**循环唤醒所有连续的SHARED节点**,直到遇到EXCLUSIVE节点或队列尾部
关键区别不在队列形态,而在节点语义与唤醒规则
同一个AQS实例可以同时存在EXCLUSIVE和SHARED节点(如读写锁),但实际使用中一般只混用于高级同步器(如ReentrantReadWriteLock)。核心差异在于:
-
节点标识:靠
nextWaiter == SHARED判断是否共享节点 - 唤醒范围:独占只唤醒一个后继;共享可能唤醒多个连续后继
- 状态判定:独占用boolean结果(是否拿到锁);共享用int结果(是否可传播)
-
中断响应:独占模式支持响应中断(
acquireInterruptibly);共享模式也支持(acquireSharedInterruptibly),但传播过程本身不响应中断
不复杂但容易忽略:队列只是容器,锁的“独占性”或“共享性”本质由同步器如何定义state、怎么实现tryXxx方法、以及如何设计唤醒链路来保证。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











