aqs的unparksuccessor方法从tail向前遍历,是因为next指针在并发入队时可能未及时更新或被置为null,而prev指针通过cas原子设置、更可靠;同时可跳过waitstatus>0(cancelled)的无效节点,确保唤醒首个有效后继。

AQS 的 unparkSuccessor 方法通过从后往前遍历同步队列,跳过已取消(CANCELLED)节点,找到第一个非取消的后继节点进行唤醒。
为什么从 tail 往前找,而不是直接看 next?
因为 next 指针在并发环境下可能滞后:节点入队时先设置 prev,再 CAS 设置 next;而取消节点会把自己 next 设为 null 或指向自身,导致直接读 node.next 可能拿到 null 或已失效节点。AQS 选择更可靠的反向路径——从队尾开始逆向扫描,利用每个节点的 prev 指针(它一旦设置就不会被其他线程修改),确保遍历过程稳定。
如何识别并跳过 CANCELLED 节点?
每个节点的 waitStatus 字段标记状态:Node.CANCELLED == 1。方法中会逐个检查候选节点的 waitStatus:
- 若
ws > 0(即ws == CANCELLED),说明该节点已被取消,继续向前找(s = s.prev) - 若
ws (如 <code>0、SIGNAL、CONDITION等),视为有效后继,尝试唤醒
为什么要尝试设置后继的 waitStatus 为 SIGNAL?
即使找到非取消节点,也不代表它一定能被安全唤醒。AQS 会先用 CAS 将其 waitStatus 设为 SIGNAL(-1):
- 成功:说明该节点正处于等待状态,可以放心 unpark
- 失败:说明该节点可能刚被取消或已唤醒,此时继续向前查找下一个有效节点
这一步是保障唤醒操作原子性和正确性的关键防护。
找不到有效后继怎么办?
如果从 node.next 开始反向遍历到 head 都没找到非取消节点,说明当前节点后面没有可唤醒的等待者。此时方法直接返回,不执行任何 unpark。后续若有新节点入队,会由新节点的 acquireQueued 或 shouldParkAfterFailedAcquire 重新建立唤醒链。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











