AQS通过“只唤醒一个”和“队列化等待”根治惊群效应。它仅唤醒CLH队列中head后继的有效节点,其余线程保持park;失败线程入队形成FIFO链,唤醒后还需双重校验才尝试获取锁;state与队列分离保障精准唤醒,共享模式下也按需唤醒。

AQS 通过“只唤醒一个”和“队列化等待”两个核心机制,从根源上规避了惊群效应,不需要额外缓解措施。
只唤醒 CLH 队列中真正该执行的线程
AQS 不使用 notifyAll() 那类广播式唤醒。当锁释放时,它调用 unparkSuccessor() 方法,严格检查同步队列(CLH 变体)中 head 节点的下一个有效节点(跳过 CANCELLED 状态),仅对那个节点对应的线程执行 LockSupport.unpark()。
其余所有等待线程仍处于 park 状态,完全不参与本次竞争——既不抢锁,也不触发上下文切换,彻底消除无效唤醒带来的抖动。
入队即排队,天然 FIFO 顺序保障公平调度
线程获取锁失败后,会被封装为 Node 节点,以 CAS 方式尾插进双向队列。这个过程不是“挂起后随机唤醒”,而是形成一条逻辑清晰的等待链。
立即学习“Java免费学习笔记(深入)”;
每个节点在被唤醒后,还需在 acquireQueued() 中自旋检查:前驱是否为 head、是否轮到自己。只有满足条件才尝试 CAS 获取 state;否则继续 park。这层双重校验屏蔽了虚假唤醒、中断干扰等异常,确保实际执行顺序与入队顺序一致。
状态与队列分离,避免资源就绪时的误唤醒
AQS 的 state 是 volatile int,所有变更都通过 CAS 原子操作,配合队列结构,让“资源可用”和“谁来消费”解耦。
对比 synchronized + wait()/notifyAll():后者依赖单一对象监视器,notifyAll() 无法区分哪些线程真正需要该资源;而 AQS 中,线程入队前已明确是为争抢同一 state,唤醒动作又精准落在队首后继,不存在“唤醒了但没活干”的情况。
共享模式下也能按需唤醒
在 Semaphore 或 CountDownLatch 场景中,AQS 同样支持共享模式(SHARED 节点)。releaseShared() 会遍历后继节点,但只唤醒那些 能成功更新 state 的线程——例如还有剩余许可证,才唤醒下一个等待者;否则停止传播。这比 signalAll() 更克制,也更贴近业务语义。



















