AtomicStampedReference 并未彻底根除 ABA 问题,而是通过版本戳将 ABA 转化为可检测的冲突;它要求引用和戳同时匹配才能 CAS 成功,从而暴露而非消除 ABA,关键在于可控处理。

AtomicStampedReference 本身不能“彻底根除”ABA问题,它只是把 ABA 从逻辑错误转化为可检测的版本冲突——关键不在消除,而在**暴露和可控处理**。
为什么 CAS 有 ABA 隐患?
CAS(Compare-And-Swap)只比对值是否相等,不关心中间是否被修改过又改回。比如一个栈顶节点被弹出(A→B),又被新节点复用为栈顶(B→A),CAS 会误认为没变,导致链表结构错乱、内存泄漏或数据丢失。
AtomicStampedReference 的设计思路很直接:给引用配上一个“版本戳”(stamp),每次修改都更新戳。即使引用值回到 A,只要 stamp 不同,CAS 就失败。
AtomicStampedReference 的核心用法
它封装了 引用 + 整数戳 的二元组,所有操作必须同时验证引用和戳:
立即学习“Java免费学习笔记(深入)”;
- 构造时需指定初始引用和初始戳(通常为 0)
- compareAndSet(oldRef, newRef, oldStamp, newStamp) 是原子操作:仅当引用等于 oldRef 且戳等于 oldStamp 时,才设为 newRef 和 newStamp
-
get() 返回
Pair<Ref, Stamp>(实际是 Object[],需解包) - weakCompareAndSet 不保证 happens-before,一般不用
典型场景:无锁栈的 ABA 防护
以实现线程安全的无锁栈为例:
- 每次 push 或 pop 操作,都基于当前 top 节点和当前 stamp 执行 CAS
- pop 时,若发现 top 被其他线程弹出又压入相同节点(ABA),stamp 已变,CAS 失败,当前线程重试
- push 时同样校验 stamp,避免基于过期快照修改
- 注意:stamp 应随每次逻辑修改递增(如用 AtomicInteger.getAndIncrement()),不能简单加1——防止整数溢出后回绕(Java 8+ 中 AtomicStampedReference 内部未做回绕防护,需业务层控制)
使用注意事项与常见误区
AtomicStampedReference 不是银弹:
- 戳不是自动管理的:你得自己决定何时、如何更新 stamp。随意复用或固定 stamp 会让机制失效
- stamp 溢出风险真实存在:int 最大值约 21 亿,高频更新场景下可能回绕。可考虑用 LongStamp(自定义类)或借助时间戳+序列号组合
- 无法解决“语义 ABA”:比如两个不同对象逻辑上等价(如 User{id=1, name="Alice"} 被删又重建),引用不同但业务意义相同——这得靠业务层识别,不是 AtomicStampedReference 的职责
- 性能略低于 AtomicReference:多一次 int 字段读写和比较,但通常可接受
不复杂但容易忽略:真正起作用的不是类本身,而是你是否在每次关键状态变更时,同步、不可分地更新引用和戳。



















