atomicreference适用于普通引用更新,atomicstampedreference通过版本戳解决aba问题;前者仅比较引用值,后者需值与版本号同时匹配才更新。

Java 中用 AtomicReference 和 AtomicStampedReference 实现安全的引用更新,核心是避免多线程下“ABA 问题”和保证 CAS 操作的原子性。前者适合普通引用更新,后者在需要版本控制(防 ABA)时使用。
AtomicReference:基础的无锁引用更新
AtomicReference 提供基于 CAS(Compare-And-Swap)的原子引用操作,适用于不需要区分“值是否真正变化过”的场景。它内部封装一个 volatile 引用,并通过 Unsafe 类调用 CPU 原子指令实现线程安全。
- 常用方法:
compareAndSet(expected, updated)—— 只有当前值等于 expected 才更新为 updated,返回 boolean 表示是否成功 - 获取最新值用
get(),设置新值用set()或lazySet()(延迟写入,性能略高) - 注意:它只比对引用是否相等(
==),不关心对象内容是否被修改过
例如实现一个线程安全的单例 holder:
AtomicReference<myservice> holder = new AtomicReference();
MyService instance = holder.get();
if (instance == null) {
instance = new MyService();
if (holder.compareAndSet(null, instance)) {
// 成功初始化
} else {
instance = holder.get(); // 其他线程已设好,取现成的
}
}</myservice>
AtomicStampedReference:带版本戳的引用更新(解决 ABA 问题)
当某个引用从 A → B → A 变化时,AtomicReference 的 CAS 会误认为没变而成功更新,这就是 ABA 问题。AtomicStampedReference 通过引入一个整型 stamp(版本号)联合判断,确保“值相同 + 版本号相同”才允许更新。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 构造时需传入初始引用和初始 stamp,如
new AtomicStampedReference(null, 0) - 关键方法:
compareAndSet(V expectedRef, V newRef, int expectedStamp, int newStamp) - 获取当前引用和 stamp 要一起调用
get(int[] stampHolder),stampHolder 数组用于输出当前 stamp
典型用法:每次更新都递增 stamp(哪怕引用值没变),防止旧值被误重用:
AtomicStampedReference<node> ref = new AtomicStampedReference(null, 0);
int[] stamp = new int[1];
Node current = ref.get(stamp);
int oldStamp = stamp[0];
// ……业务逻辑处理……
if (!ref.compareAndSet(current, newNode, oldStamp, oldStamp + 1)) {
// 失败:期间被其他线程修改过,重试或放弃
}</node>
选择依据:什么时候用哪个?
是否需要防范 ABA,是决定用哪个类的关键。
- 如果引用指向的对象是不可变的(如 String、Integer),或业务逻辑不依赖中间状态变化,用
AtomicReference更简洁高效 - 如果引用指向可变对象,且存在“被弹出 → 修改 → 重新压入”这类链表/栈操作(如无锁栈、队列),必须用
AtomicStampedReference或AtomicMarkableReference - 注意:
AtomicStampedReference的 stamp 是 int 类型,可能溢出;若需更大范围版本控制,可考虑自定义结构或用LongAdder辅助管理
实际使用中的常见注意事项
这两个类本身线程安全,但不能自动保护其引用对象的内部状态。
- 引用的对象如果是可变对象,仍需保证其字段访问是线程安全的(比如字段加 volatile、用 synchronized 或其他原子类)
- CAS 是乐观策略,失败后通常要循环重试(即“自旋”),注意避免长时间自旋导致 CPU 浪费,必要时加入短暂让出(
Thread.yield())或退避策略 - 慎用
getAndSet()或accumulateAndGet()等复合操作,确认它们语义符合业务需求,不要假设它们“绝对原子”——它们只是对引用本身原子,不包含业务逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










