enq方法通过cas自旋死循环确保节点最终入队成功:先用cas创建虚拟头节点(若队列为空),再cas更新tail并手动设置prev/next指针,保障双向链表结构完整且唤醒逻辑统一。

enq 方法通过无限 for 循环 + CAS 尝试,确保节点最终被插入队列尾部。它不依赖锁或阻塞,而是靠反复检查和原子更新来应对并发竞争。
自旋的核心逻辑:循环重试直到 CAS 成功
enq 内部是典型的“CAS 自旋”结构:
- 每次循环都重新读取 tail 当前值(volatile 读),避免使用过期引用
- 如果 tail 为 null,说明队列未初始化,先用 CAS 创建一个空 head 节点,并让 tail 指向它
- 如果 tail 不为 null,就尝试用 CAS 把 tail 从旧值 t 改成新节点 node
- CAS 失败说明有其他线程抢先修改了 tail,循环继续,下次再读新 tail 值重试
为什么能保证成功?关键在 CAS 的原子性与循环的兜底性
CAS 是硬件级原子指令,要么完整成功,要么完全失败,不会出现中间态。而自旋不设次数上限,只要线程没被中断或调度挂起,就持续尝试。在真实场景中:
- 低竞争时,往往第一次 CAS 就成功
- 高竞争时,虽然可能多试几次,但因线程分散在不同 CPU 核心,冲突概率有限,通常 1–3 次内完成
- 即使多个线程同时竞争 tail,也总有一个会成功;失败者自动进入下一轮,不会丢失入队意图
prev 和 next 的安全设置顺序
enq 在 CAS 前就设置 node.prev = t,这个操作只影响当前 node,无竞争风险;CAS 成功后才设置 t.next = node。这种“先连前驱、后连后继”的顺序,既避免了双向链表断裂,又让队列结构在任意时刻都保持可遍历性——哪怕 CAS 还没完成,node 已有 prev,后续也能定位到它。
和 addWaiter 的分工:快速路径 + 兜底保障
addWaiter 先走“快速路径”:直接读 tail 并 CAS,成功就返回;失败才调 enq。enq 是兜底实现,它处理两种情况:
- 队列尚未初始化(tail == null)
- 快速路径因 CAS 竞争失败(tail 已被其他线程更新)
也就是说,enq 不追求快,而追求稳——它把所有边界条件都收口在同一个自旋块里,彻底消除竞态遗漏。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











