atomicreference 仅保证引用读写原子性,不保护对象内部状态;适用引用替换场景,不适用于内部字段修改或复合操作;应优先用 compareandset() 而非 weakcompareandset();更新内部字段需循环 cas 构造新对象;相比 volatile,它提供原子更新能力而非仅可见性。

AtomicReference 为什么不能直接替换 synchronized?
它只保证引用本身的读写原子性,不保证对象内部状态的线程安全。比如用 AtomicReference<list>></list> 存一个 ArrayList,多个线程调用 list.add() 依然会出并发问题——因为 add() 是对象自己的方法,不在 AtomicReference 的保护范围内。
常见错误现象:NullPointerException 或数据丢失,表面看是 CAS 失败,实际是误以为“换了 AtomicReference 就万事大吉”。
- 适用场景:需要无锁更新整个引用(比如切换配置对象、状态机切换、链表头节点)
- 不适用场景:频繁修改对象内部字段、需要复合操作(如先 get 再 update 再 set)
- 性能影响:比
synchronized在低争用时更快,但高争用下 CAS 自旋可能浪费 CPU
compareAndSet() 和 weakCompareAndSet() 到底该用哪个?
weakCompareAndSet() 不保证内存顺序(JVM 可能重排),且不保证一定成功(即使条件满足也可能失败),仅用于极少数无锁算法的底层实现,比如 ConcurrentLinkedQueue 内部。日常业务代码一律用 compareAndSet()。
容易踩的坑:weakCompareAndSet() 返回 false 不能直接当作“值已变”来处理,它可能是假失败;而 compareAndSet() 的语义明确:值相等就替换,返回布尔结果可直接用于控制流。
立即学习“Java免费学习笔记(深入)”;
- 参数差异:
compareAndSet(expected, desired)中expected是当前期望值,必须严格等于内存中值才成功 - 注意引用相等性:比较的是对象引用,不是内容。若想按内容比较,得自己封装逻辑(比如用
AtomicReference<atomicinteger></atomicinteger>) - 兼容性:所有 JDK 版本都支持
compareAndSet();weakCompareAndSet()在 JDK 9+ 才有更强语义(但依然慎用)
怎么安全地更新对象内部字段?
不能靠 AtomicReference 自动管理,得把“读-改-写”变成原子操作。最常用的是循环 CAS 模式(loop-CAS):
AtomicReference<User> ref = new AtomicReference<>(new User("a", 0));
User current;
User update;
do {
current = ref.get();
update = new User(current.name, current.age + 1); // 不改原对象
} while (!ref.compareAndSet(current, update));
关键点在于:每次构造新对象,避免共享可变状态;用 compareAndSet() 重试直到成功。
- 如果对象字段多、构造开销大,考虑用
AtomicIntegerFieldUpdater等字段级原子类替代 - 禁止在循环里做耗时操作(如 IO、锁、复杂计算),否则会卡住其他线程的 CAS 尝试
- 注意 ABA 问题:虽然引用没变,但中间被替换成别的对象又换回来。一般业务场景影响小,金融/底层结构需配合
AtomicStampedReference
和 volatile 引用比,AtomicReference 多了什么?
volatile 只保证可见性和禁止重排序,不提供原子更新能力。比如 volatile User user,你不能原子地执行 “如果 user != null 就 user.setName(‘x’)”,因为判断和赋值是两步。
AtomicReference 提供了 compareAndSet()、getAndSet()、updateAndGet() 这些带原子语义的操作,这才是它存在的核心价值。
- 性能差异:两者读操作成本接近;
AtomicReference的写操作略重(涉及 CAS 指令),但换来的是确定的原子行为 - 使用场景错位:
volatile适合“只读发布”或“单次写入后只读”,AtomicReference适合需要反复更新引用的场景 - 别混用:不要对同一个变量既加
volatile又包进AtomicReference,纯属冗余
真正难的从来不是记住 API,而是判断什么时候不该用它——比如对象本身不可变,那 volatile 就够了;比如操作本质是 I/O 密集型,锁反而更稳。原子引用不是银弹,它只解决引用层面的原子性这一个点。










