atomicreference 实现无锁栈的核心思想是利用 cas 原子操作替代锁,通过 while 循环重试确保 push(头插)和 pop(头删)的线程安全,需注意 aba 问题、node 不可变性及避免循环内耗时操作。

AtomicReference 实现无锁栈的核心思想
无锁栈的关键在于不使用 synchronized 或 ReentrantLock,而是依靠 AtomicReference 的 compareAndSet(CAS)原子操作来保证多线程下栈顶节点的更新安全。栈结构由链表构成,每个节点包含数据和指向下一个节点的引用,栈顶由一个 AtomicReference
Push 操作:头插法 + CAS 循环
压栈即在链表头部插入新节点。逻辑是:读取当前栈顶 node,构造新节点使其 next 指向该 node,再用 CAS 尝试将栈顶更新为新节点。
- 若 CAS 成功,说明期间无人修改栈顶,插入完成
- 若 CAS 失败,说明其他线程已抢先更新了 top,需重新读取最新 top,再次构造并重试
- 典型实现中会用 while(true) 循环包裹 CAS,确保最终成功
Pop 操作:头删 + CAS 循环 + ABA 问题应对
弹栈需取出栈顶节点并更新 top 指向其 next。同样依赖 CAS:记录当前 top 和 top.next,尝试将 top 从旧值更新为 top.next。
- CAS 成功则返回原栈顶节点的数据,操作完成
- CAS 失败则重读 top,继续循环
- 注意潜在的 ABA 问题:top 被改为 B 又改回 A,CAS 误判为未变。实际生产中可结合 AtomicStampedReference 增加版本戳,但纯 AtomicReference 实现通常假设栈元素不会被重复回收复用(如使用对象池时需额外防护)
关键细节与常见陷阱
无锁栈看似简洁,但几个细节极易出错:
- Node 类必须是不可变的 next 引用(或至少 next 在构造后不被外部修改),否则破坏链表一致性
- Pop 返回 null 表示栈空,但需注意:即使 top 为 null,CAS 设置为 null 仍可能失败(因有竞争),所以判断空栈应以读取到的 top == null 为准,而非依赖某次 CAS 结果
- 不能在 CAS 循环内做耗时操作(如 I/O、复杂计算),否则导致重试开销大、吞吐下降
- 内存可见性由 AtomicReference 的 volatile 语义保障,无需额外同步











