AtomicStampedReference 不自动解决ABA问题,而是通过原子绑定引用与版本号使ABA可检测;必须用get(int[])原子读取值和stamp,不可分开调用;stamp由用户控制、需每次重读并正确递增;它不消除ABA,只让CAS暴露中间修改,适用于物理地址复用场景。

AtomicStampedReference 不是“自动解决” ABA 问题的黑盒,而是把识别权交给你——它通过原子绑定引用值和版本号(stamp),让“值没变但中间被改过”这件事可被检测。关键不在加了个 int,而在你是否严格按原子对的方式读、算、写。
必须用 get(int[]) 原子读取当前值与 stamp
分开调用 getReference() 和 getStamp() 是常见错误:两次调用之间可能被其他线程修改,导致拿到的值和 stamp 来自不同快照,CAS 必然失败或误成功。
- 正确做法:声明 int[] stamp = new int[1],再调用 ref.get(stamp),此时返回的引用与 stamp[0] 严格对应
- 这个数组是唯一安全入口,JDK 故意不提供其他便捷方式,就是防止绕过一致性校验
- Android API 24 以下不支持该方法,反射调用会抛 NoSuchMethodError
每次 compareAndSet 都要基于最新 stamp 计算 newStamp
stamp 不自动递增,也不自带语义,完全由你控制。硬编码 newStamp(比如写死为 1 或 oldStamp + 1 却不重读)会导致 CAS 永远失败。
- 典型安全模式:读出 current 和 stamp[0] → 构造 compareAndSet(current, next, stamp[0], stamp[0] + 1)
- 在循环重试中,每轮都必须重新 get(),不能缓存 stamp[0] 多次使用
- 如果一次业务操作涉及多个状态跃迁(如 INIT → PROCESSING → DONE),可用固定值(0/1/2)代替自增,更贴合语义
它不消除 ABA,只让 ABA 可拦截
ABA 的本质不是数值变化,而是状态可信度丢失。比如无锁栈中节点被弹出又压回,表面引用相同,实际内存已被复用或逻辑上下文已断裂。
立即学习“Java免费学习笔记(深入)”;
- AtomicStampedReference 不阻止 A→B→A,但确保 CAS 在 stamp 变更时失败,从而暴露中间修改
- 适用场景明确:对象池节点复用、链表指针变更、缓存 key 被回收重用等“物理地址复用”类风险
- 若业务上 A↔A 是合法行为(如开关反复切换),加版本号反而增加无谓开销
注意 stamp 溢出与性能边界
stamp 是 int 类型,从 Integer.MAX_VALUE 回绕到 Integer.MIN_VALUE 极少见,但超长生命周期+高频更新时需评估 wrap-around 是否可接受。
- 实测吞吐量比 AtomicReference 低 5%–15%,主要因多一次字段读写及缓存行压力
- 底层仍是 Unsafe.compareAndSwapObject,但操作的是 Pair 对象(含 reference + stamp),非单字段
- 若服务要求绝对单调,可考虑 AtomicMarkableReference(boolean 标记)或自定义 long 版本号



















