AtomicStampedReference通过将引用值与版本号(stamp)封装为原子pair,CAS操作必须同时匹配二者,从而检测ABA:即使值恢复原样,stamp变更也会导致CAS失败,确保中间修改可感知。

AtomicStampedReference 通过引入版本号(stamp)来区分“相同值但不同历史”的情况,从而在逻辑上解决 ABA 问题。它不否定 ABA 的物理发生,而是让调用方能感知到中间是否发生过修改——哪怕值又变回原样,stamp 也已不同。
Stamp 是怎么和值绑定并一起原子更新的
AtomicStampedReference 内部用一个 pair 对象(如 Pair<T, Integer>)同时封装引用值和版本号,CAS 操作不是只比对引用,而是比对整个 pair:只有当前引用 == 预期引用 且 当前 stamp == 预期 stamp,才更新成功。这就保证了“值没变但 stamp 变了”会被拒绝。
- 每次调用
compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp),底层都做一次 pair 级别的 CAS -
get()返回的是new Pair<V, Integer>(reference, stamp),避免拆包时被其他线程干扰 - 典型用法是先
get()拿到当前值和 stamp,再基于它们构造期望参数去 CAS
为什么单纯靠值比较无法发现 ABA
假设链表头节点 A → B → C,线程1读到 head = A;线程2把 A 弹出,插入新节点 D,再把 A 放回头部(A ← D → B → C),此时 head 又是 A。线程1执行 CAS(A→X) 会成功,但它不知道 A 已被“动过”。而 AtomicStampedReference 中,只要 A 被弹出过,stamp 就会加 1(或变更),即使最后 head 值仍是 A,stamp 也已不同,CAS 失败。
- stamp 不要求递增,只要每次修改都变更即可(常用 AtomicInteger 自增)
- 关键不是 stamp 本身含义,而是它作为“状态指纹”,能唯一标识某次修改事件
- 如果 stamp 重复使用(比如只用 0/1 交替),仍可能漏判,所以建议用自增整数
实际使用中 stamp 的维护策略
stamp 由业务逻辑控制,AtomicStampedReference 不自动管理其语义。常见做法是:每次对目标对象做结构性修改(如入队、出队、链接变更),就调用 set() 或 compareAndSet() 同时更新值和 stamp,并让 stamp 严格递增。
立即学习“Java免费学习笔记(深入)”;
- 例如实现无锁栈:每次 push/pop 都让 stamp +1,即使操作后栈顶又回到同一对象
- 不要复用旧 stamp,也不要跨操作共享 stamp;每个 CAS 尝试应基于自己上次读到的 stamp
- 可配合版本号生成器(如
new AtomicInteger())统一分配,避免手写错误
它不能解决所有“逻辑 ABA”,但划清了责任边界
AtomicStampedReference 解决的是“值相同但中间态不可见”这一层 ABA,属于并发原语层面的修复。它不保证业务语义正确——比如 stamp 正确但业务上该操作已被废止,这需上层额外校验(如加时间戳、业务状态字段)。
- 它让 ABA 可检测,而非自动规避;是否拒绝该操作,由业务决定
- 相比 Unsafe + 自定义标记位,它提供标准、线程安全的 stamp 管理机制
- 若业务无需区分历史(如计数器仅关心最终值),用 AtomicInteger 更轻量


















