AtomicStampedReference通过将引用与版本号封装为原子Pair并要求CAS同时校验二者,使线程能感知ABA而非阻止其发生;stamp由业务严格维护,每次状态变更必须更新,确保“值同而历史异”可被检测。

Java 中 CAS 操作本身无法避免 ABA 问题,它只比对数值是否相等,不记录修改历史。要防止状态误判,关键是在 CAS 基础上叠加“可识别中间变更”的机制——最常用、最直接的方式是用 AtomicStampedReference 引入版本号校验。
为什么单纯 CAS 会误判状态
CAS 的判断逻辑极简:只要当前值等于你读到的旧值,就允许更新。但它完全无视这期间是否发生过 A→B→A 的来回改动。比如一个节点被弹出、释放、又被复用为新节点,地址没变但语义已完全不同;CAS 看到引用还是那个引用,就贸然操作,结果可能访问已释放内存或跳过应处理逻辑。
AtomicStampedReference 是怎么防住的
它把目标引用和一个整型版本号(stamp)绑定成原子单元,每次 compareAndSet 都必须同时满足两个条件:
- 引用值与预期一致
- 版本号也与预期一致
只要中间发生过任何一次修改,stamp 就会递增。哪怕值绕了一圈回到原样,stamp 已不同,CAS 就失败,迫使线程重新读取最新状态再决策。
正确使用的三个实操要点
版本号不是自动增长的,必须由你显式维护,且必须基于最新快照:
- 用 get(int[] stampHolder) 一次性读出当前引用和 stamp,不能分开调用 getReference() 和 getStamp()
- 每次 compareAndSet 的 newStamp 必须基于这次读出的 stamp 计算(如 stamp[0] + 1),不能硬编码或复用旧值
- 在循环重试逻辑中,每次 CAS 失败后必须重新 get(),否则拿过期 stamp 去比,永远失败
它适合什么场景,又不适合什么
AtomicStampedReference 解决的是“引用被复用”类 ABA,典型如无锁栈/队列的节点指针、对象池中的实例、带生命周期的缓存条目。
如果业务上 A→B→A 是合法且无副作用的操作(比如开关反复切换、状态在多个有效值间震荡),加版本号反而增加开销,此时无需强防 ABA。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











