atomicreference适合更新状态机式对象(如state枚举)和不可变对象(如final字段类),因其仅保证引用本身原子性,不保证对象内容线程安全。

AtomicReference 适合更新什么类型的数据
它只保证「引用本身」的原子性,不是对象内容的原子性。比如你用 AtomicReference<user></user>,compareAndSet 成功只说明「这个变量此刻指向的是旧 User 实例、现在换成了新 User 实例」,但 User 里 name 或 age 字段改没改、怎么改的,它完全不管。
所以它适合两种场景:状态机式对象(如 State 枚举)、不可变对象(如 ImmutableList 或自己写的 final 字段类)。一旦你传进去的是可变对象(比如普通 User),后续还去改它的字段,就和 AtomicReference 没关系了——并发问题照旧。
- ✅ 推荐:用
final字段 + 全参数构造器定义对象,每次更新都 new 一个新实例 - ❌ 避免:把
new User()塞进去后,又调user.setName("x") - ⚠️ 注意:
AtomicReference不会阻止你干这事,但后果得你自己兜着
compareAndSet 怎么写才不踩空指针或ABA
compareAndSet 是核心操作,但它不是万能锁。常见错误是拿 null 当期望值却没处理好初始化,或者在循环里反复重试时忽略了 ABA 问题(比如值从 A→B→A,CAS 以为没变就通过了)。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 初始化别裸写
new AtomicReference(),优先用new AtomicReference(initialValue),避免第一次get()就 null - 如果业务逻辑允许 ABA(比如只是开关状态),那不用管;否则得换
AtomicStampedReference加版本戳 - 不要在
compareAndSet失败后直接 return,典型模式是 while 循环重试:while (true) {<br> User old = ref.get();<br> User updated = new User(old.getName(), old.getAge() + 1);<br> if (ref.compareAndSet(old, updated)) break;<br>}
和 volatile / synchronized 比,什么时候该选它
三者解决的问题层次不同:volatile 只保证可见性和有序性,不保证复合操作原子性;synchronized 能包住任意逻辑,但有锁开销;AtomicReference 是无锁的 CAS,适合「读多写少 + 更新逻辑简单」的场景。
- ✔️ 适合:
AtomicReference<boolean></boolean>做一次性标记(如初始化完成)、AtomicReference<node></node>实现无锁栈/队列节点替换 - ✖️ 不适合:要同时更新两个字段(如
balance和version),这时得上AtomicReferenceFieldUpdater或直接锁 - ⚠️ 性能提示:高争用下 CAS 可能自旋很久,反而不如
synchronized稳定,JDK 8 后 synchronized 优化很多,别迷信“无锁一定快”
自定义对象必须重写 equals 吗
不需要。因为 AtomicReference 的 compareAndSet 用的是 == 判断引用相等,不是 equals。你传进去两个内容相同但不同实例的对象,只要不是同一个对象,compareAndSet 就不会成功。
- 这是设计使然,不是 bug —— 它本来就是基于引用的原子更新
- 所以你写
new User("a", 1)和new User("a", 1),哪怕equals返回 true,compareAndSet也认不出来 - 如果你真需要按内容比较再更新,说明这不是 AtomicReference 的使用场景,该考虑数据库乐观锁或应用层协调机制
最常被忽略的一点:很多人以为把对象塞进 AtomicReference 就自动线程安全了,其实只是“换引用”这一步安全了。对象内部是否可变、谁在什么时候改它、改了之后其他线程看不看得见——这些全得自己想清楚、控得住。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










