atomicreference仅保证引用替换原子性,不保障对象内部字段线程安全;适用不可变自定义对象,需配合cas循环重试,禁用可变实例和非原子字段修改。

AtomicReference 能安全替换自定义对象,但只保“换引用”这一步原子,不保对象内部字段线程安全。它不是万能锁替代品,而是针对“单个引用变量的无锁更新”这一窄场景设计的高效工具。用对了轻量高效,用错了反而掩盖一致性风险。
自定义对象怎么写才适合 AtomicReference
必须让对象本身支持“整体替换”,核心是不可变(immutable):
- 所有字段声明为
final - 构造器一次性传入全部参数,不提供任何
setter - 提供
withXxx()工厂方法,每次返回新实例(例如order.withStatus(CONFIRMED)) - Java 14+ 直接用
record,天然满足上述要求,推荐优先采用
反例:塞一个普通 Order 对象进去,后续再调 order.setAmount(100) —— AtomicReference 完全不干预,线程安全由你自行兜底,极易出错。
CAS 更新必须带循环重试
AtomicReference 不自动重试,compareAndSet 失败就立刻返回 false。要实现可靠更新,必须手动加循环:
- 先
get()拿当前引用 - 基于当前状态计算出新对象(新建不可变实例)
- 用
compareAndSet(current, next)尝试替换 - 失败则重新
get(),再算、再试
典型写法:
OrderState current = ref.get();while (!ref.compareAndSet(current, current.withStatus(OrderStatus.CONFIRMED))) {
current = ref.get();
}
无锁单例的正确姿势
AtomicReference 可用于实现真正无锁读取的懒汉式单例,关键点在于“初始化仅一次 + 发布即可见”:
- 用
new AtomicReference<singleton>()</singleton>初始化,避免首次get()返回null导致 CAS 失败 - 创建实例后,用
compareAndSet(null, instance)原子发布;失败说明已被其他线程初始化,直接返回已存实例 - 后续所有读取都是无锁
get(),零开销 - 注意:不能在
compareAndSet成功前暴露未完全构造的对象(如 C++ 中需配对memory_order_release/acquire),Java 中只要对象构造完成再发布,JVM 内存模型可保证安全
哪些场景别硬套 AtomicReference
它不是通用并发解决方案,以下情况应换思路:
- 需要同时更新多个强关联字段(如订单金额 + 状态 + 时间戳)→ 封装为不可变对象再用 AtomicReference,或改用
AtomicReferenceFieldUpdater+ 锁 - 高争用且更新逻辑较重(比如每次要解析 JSON 或查 DB)→ 自旋重试开销大,
synchronized反而更稳 - 要求可重入、超时等待、中断响应 → 它不支持,该用
ReentrantLock - 存在 ABA 敏感操作(如资金流水号回绕)→ 改用
AtomicStampedReference加版本号











