AQS独占锁如何保障线程调度公平性,有效分析其出队顺序以缓解惊群效应?
- 内容介绍
- 相关推荐
本文共计823个文字,预计阅读时间需要4分钟。
AQS通过不依赖`notifyAll()`或`broadcast()`,而是采用严格的CLH队列的FIFO顺序,只唤醒对应节点的单个线程,从而实现惊群效应的避免。即在资源争抢时,大量线程被同时唤醒、竞争,导致大量失败退回——这在AQS中不会发生。
关键在于:unparkSuccessor(Node) 方法内部只取队列中第一个有效后继节点调用 LockSupport.unpark(),其余线程仍在 park 状态,完全不参与本次竞争。这和传统 Object.wait()/notifyAll() 的粗粒度唤醒有本质区别。
acquireQueued(Node, int) 中的自旋+阻塞组合如何防止虚假唤醒干扰公平性
线程进入队列后,并非一被 unpark 就直接执行业务,而是先自旋检查前驱是否为头节点(pred == head),仅当满足才尝试 CAS 获取 state;否则继续 park。这个双重校验机制屏蔽了以下干扰:
-
LockSupport.unpark()提前调用导致的“空唤醒” - 其他线程释放锁时误唤醒非后继节点(AQS 本身不会,但 JVM 层或信号可能干扰)
- 中断状态未清导致的非预期返回
也就是说,即使线程被意外唤醒,只要它不是队列中下一个该轮到的节点,就会立刻重新 park,不破坏 FIFO 次序。
公平锁与非公平锁在出队逻辑上是否一致
出队唤醒逻辑完全一致:都是从 head 开始,唤醒 head.next。
本文共计823个文字,预计阅读时间需要4分钟。
AQS通过不依赖`notifyAll()`或`broadcast()`,而是采用严格的CLH队列的FIFO顺序,只唤醒对应节点的单个线程,从而实现惊群效应的避免。即在资源争抢时,大量线程被同时唤醒、竞争,导致大量失败退回——这在AQS中不会发生。
关键在于:unparkSuccessor(Node) 方法内部只取队列中第一个有效后继节点调用 LockSupport.unpark(),其余线程仍在 park 状态,完全不参与本次竞争。这和传统 Object.wait()/notifyAll() 的粗粒度唤醒有本质区别。
acquireQueued(Node, int) 中的自旋+阻塞组合如何防止虚假唤醒干扰公平性
线程进入队列后,并非一被 unpark 就直接执行业务,而是先自旋检查前驱是否为头节点(pred == head),仅当满足才尝试 CAS 获取 state;否则继续 park。这个双重校验机制屏蔽了以下干扰:
-
LockSupport.unpark()提前调用导致的“空唤醒” - 其他线程释放锁时误唤醒非后继节点(AQS 本身不会,但 JVM 层或信号可能干扰)
- 中断状态未清导致的非预期返回
也就是说,即使线程被意外唤醒,只要它不是队列中下一个该轮到的节点,就会立刻重新 park,不破坏 FIFO 次序。
公平锁与非公平锁在出队逻辑上是否一致
出队唤醒逻辑完全一致:都是从 head 开始,唤醒 head.next。

