CAS在原子更新引用类型时,通过volatile字段保证可见性与有序性,结合Unsafe.compareAndSwapObject指令对引用值进行原子比较与替换,确保多线程下引用更新的原子性;其比较的是对象实例地址(==语义),不涉及对象内部状态安全。

CAS 在原子更新引用类型时,并不直接保证“对象地址安全”,而是通过引用值的原子性比较与替换,配合 volatile 内存语义,确保多线程环境下对同一引用变量的读写具备可见性、有序性和原子性。所谓“地址安全”,本质是防止多个线程同时修改该引用导致丢失更新或读到中间状态,而非保护对象内存地址本身不被篡改(Java 中对象地址由 JVM 管理,不可直接操作)。
引用类型的 CAS 依赖三个关键要素
以 AtomicReference<V> 为例,其底层 CAS 操作针对的是引用变量在内存中的存储位置(即该字段的内存偏移量),而非对象内容或地址:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- volatile 修饰的引用字段:AtomicReference 内部用 volatile 字段保存当前引用值,确保每次读取都从主内存获取最新值,写入立即刷新到主内存,避免线程本地缓存导致的不可见问题。
-
CAS 的三元操作:调用
compareAndSet(expected, newReference)时,JVM 通过Unsafe.compareAndSwapObject指令,原子地检查该字段当前值是否等于expected(即引用是否指向同一个对象实例),若是,则替换成newReference;否则失败返回 false。 -
引用值的语义一致性:CAS 比较的是引用的“值”——也就是对象在堆中的起始地址(JVM 层面的指针值)。只要两个引用指向同一个对象实例(
==成立),就认为相等;这与equals()无关,也不关心对象内部状态是否改变。
为什么不会出现“地址被意外覆盖”?
Java 不允许用户直接操作对象内存地址,所有对象引用均由 JVM 统一管理。AtomicReference 的 CAS 操作仅作用于它所封装的那个引用字段(如一个 private volatile Object ref),其他线程无法绕过 CAS 直接写入该字段——因为:
- 该字段被声明为
private volatile,外部不可直接访问; - 所有更新必须通过
set()、compareAndSet()等原子方法,这些方法最终都委托给Unsafe的原子指令; - CPU 级别的
CMPXCHG(或对应指令)保证了“读-比-写”这一整套动作不可分割,即使在多核 CPU 上也不会被中断或交错。
需要注意的边界情况
虽然引用值的替换是安全的,但对象自身状态未必线程安全:
- 若通过 AtomicReference 替换的是一个可变对象(如
AtomicReference<List>指向 ArrayList),CAS 只保证“换 List 实例”这一步原子,而 List 内部的 add/remove 仍需额外同步; - ABA 问题依然存在:若引用先指向 A 对象,后被其他线程改为 B,再改回 A,CAS 会误认为未变化而成功。此时应选用
AtomicStampedReference增加版本戳; - 不能替代锁解决复合操作问题。例如“先 get 再基于其值计算 new 值”,仍需循环重试(自旋)或结合其他同步机制。

















