atomicstampedreference 通过绑定引用与版本号实现aba问题的显式检测,不阻止a→b→a但确保cas时能发现中间修改;需原子读取值和stamp、每次cas前重读stamp并递增,仅适用于物理地址复用等特定场景。

Java 中 ABA 问题不能靠“自动修复”解决,AtomicStampedReference 的作用是让 ABA 可被明确检测——它不阻止 A→B→A 的发生,但确保你执行 CAS 时能立刻发现中间已被修改过。
为什么值相同也不代表安全
线程1读到引用 A 后暂停;线程2把 A 弹出、处理、再把同一对象 A 放回原位置。表面看 head 还是 A,但该节点内存可能已被复用、关联数据已清空、或逻辑上下文彻底断裂。普通 AtomicReference 的 CAS 会成功,而业务却可能基于错误假设运行。
AtomicStampedReference 把引用和一个整数 stamp 绑定为不可拆分的原子对。只要中间发生任何修改(哪怕最后又改回原值),stamp 就必须变更,CAS 就会因 stamp 不匹配而失败。
必须原子读取值和版本号
常见错误是分开调用 getReference() 和 getStamp():两次调用之间可能被其他线程更新,导致拿到的值和 stamp 来自不同快照,后续 CAS 必然误判。
- 正确做法:声明 int[] stamp = new int[1],再调用 ref.get(stamp)
- 返回的引用值与 stamp[0] 严格对应,这是唯一安全入口
- Android API 24 以下不支持该方法,反射调用会抛 NoSuchMethodError
版本号要每次重读、递增并用于 CAS
stamp 不自动增长,也不自带语义,完全由你控制。硬编码 newStamp = oldStamp + 1 却不重读当前 stamp,会导致 CAS 永远失败。
- 每次 compareAndSet 都要基于刚读出的 stamp 值计算 newStamp,例如 stamp[0] + 1
- 在循环重试中,每轮开头必须重新调用 ref.get(stamp),不能缓存 stamp[0]
- 若状态跃迁有明确阶段(如 INIT → PROCESSING → DONE),可用固定语义值(0/1/2)代替自增,更清晰且避免溢出顾虑
它只解决特定场景的 ABA
AtomicStampedReference 针对的是“物理地址复用”类风险:对象池节点被回收再分配、无锁链表指针被复用、缓存 key 被清除后重建等。
如果业务上 A↔A 是合法行为(比如开关反复切换、计数器归零重启),加版本号反而引入不必要开销和复杂度。此时普通 AtomicReference 更轻量高效。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











