aqs通过统一队列和state管理抽象独占与共享模型:独占模式用tryacquire/tryrelease,只唤醒一个后继;共享模式用tryacquireshared/tryreleaseshared,支持传播唤醒多个后继,并以node.shared标记区分节点类型。

Java AQS(AbstractQueuedSynchronizer)通过统一的同步队列和状态管理机制,抽象出独占与共享两种资源征用模型。其设计核心不在于重复实现排队、唤醒等底层逻辑,而在于将“是否允许多线程同时持有”这一语义差异,下沉到 tryAcquire/tryAcquireShared 等模板方法中,由子类决定资源分配规则。
独占锁的获取与释放流程
以 acquire(int arg) 为入口:
- 先调用
tryAcquire(arg)尝试获取锁;若成功(返回 true),直接结束,线程继续执行 - 若失败,则调用
addWaiter(Node.EXCLUSIVE)将当前线程封装为独占模式节点,插入同步队列尾部 - 进入
acquireQueued(node, arg)自旋:仅当该节点前驱是头节点且再次调用tryAcquire成功时,才将自身设为头节点并退出;否则挂起(LockSupport.park()) - 释放时调用
release(int arg):先执行tryRelease(arg)修改 state;若成功,唤醒同步队列中头节点的首个有效后继节点(unparkSuccessor(head))
共享锁的获取与释放流程
以 acquireShared(int arg) 为入口:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 调用
tryAcquireShared(arg),返回值决定后续行为:
• ≥ 0:表示获取成功,且可能还有剩余资源供后续线程使用,直接返回
• < 0:表示获取失败,进入等待队列 - 失败时调用
doAcquireShared(arg):同样入队(addWaiter(Node.SHARED)),自旋检查前驱是否为头节点,并反复尝试tryAcquireShared - 释放时调用
releaseShared(int arg):执行tryReleaseShared(arg)后,会遍历同步队列,**唤醒所有处于共享模式且 waitStatus 允许的后续节点**(关键区别:不是只唤醒一个)
关键设计差异点
这些差异决定了两种模式的行为边界:
- state 含义不同:独占锁通常用 state=0/1 表示是否被占用(可重入则 >1);共享锁中 state 往往代表可用许可证数(如 Semaphore)、读锁计数(如 ReentrantReadWriteLock 高16位)等可累加资源量
- 唤醒策略不同:独占锁释放后只唤醒一个后继;共享锁释放后需传播唤醒——因为多个线程可能同时满足条件,需确保“有资源就尽量分发”,避免漏唤醒
-
节点标记不同:Node 的
nextWaiter字段用于区分模式——== Node.SHARED表示共享节点,== null或== Node.EXCLUSIVE(实际为 null)表示独占节点 -
中断与超时支持粒度不同:AQS 提供
acquireInterruptibly和tryAcquireNanos等变体,但共享模式下因涉及传播,中断处理更复杂(例如部分线程已获锁,部分被中断)
为什么这样设计?
AQS 不做业务判断,只提供可靠队列+原子状态+线程调度三件套。它把“谁可以拿”“拿几个”“拿完要不要通知别人”全部交给子类实现,从而支撑 ReentrantLock(独占)、Semaphore(共享)、CountDownLatch(共享)、ReentrantReadWriteLock(混合)等多样化同步组件。这种基于模板方法 + CAS + FIFO 队列的设计,既保证了通用性,又避免了每个工具重复造轮子。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










