cas本身不保证可见性,必须配合volatile才能实现线程间变量的可见性与原子性协同;volatile提供内存可见性基础,cas提供操作原子性,二者缺一不可。

CAS(Compare-And-Swap)本身不保证可见性,必须配合 volatile 才能正确实现线程间变量的可见性与原子性协同。
volatile 为 CAS 提供内存可见性基础
CAS 操作(如 AtomicInteger.compareAndSet())底层依赖 CPU 的原子指令(如 x86 的 cmpxchg),该指令天然具有“读-改-写”原子性,但仅限于**单次操作的执行不被中断**。它并不自动让其他线程立即看到修改结果——这需要 Java 内存模型(JMM)层面的约束。
volatile 关键字对变量施加了两条关键语义:
- 写 volatile 变量时,会将工作内存中该变量及之前所有写操作的结果**刷新到主内存**;
- 读 volatile 变量时,会**强制从主内存重新加载**该变量及之后所有读操作所依赖的数据(禁止重排序)。
而 AtomicInteger 等原子类的内部值(如 value 字段)正是用 volatile 声明的:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
CAS 操作依赖 volatile 读/写来同步状态
以 compareAndSet(expected, newValue) 为例,其逻辑本质是:
- 先用 volatile 读取当前值(确保看到最新主内存值);
- 在寄存器中比较是否等于
expected; - 若相等,则用 CPU 原子指令尝试写入
newValue; - 该写入操作也具备 volatile 写的语义(由 JVM 对 volatile 字段的 CAS 实现保障)。
如果没有 volatile,线程可能一直缓存旧值在本地工作内存中,导致 CAS 总是拿过期值去比较,从而失败或产生逻辑错误(比如 ABA 问题虽另当别论,但可见性缺失会让问题更隐蔽)。
不能只靠 volatile 或只靠 CAS
二者分工明确,缺一不可:
- 仅用
volatile:能保证可见性和禁止重排序,但无法保证复合操作(如“读-改-写”)的原子性; - 仅用 CAS(无 volatile 支持):单次 CAS 是原子的,但其他线程无法及时感知变更,后续读可能仍命中旧缓存,破坏正确性;
- volatile + CAS:volatile 提供“看到最新值”的能力,CAS 提供“安全更新值”的能力,二者结合才构成线程安全的无锁编程基石。
实际开发中直接使用原子类即可
你无需手动写 volatile + CAS 组合。Java 并发包已封装好:
-
AtomicInteger、AtomicReference等内部均用volatile字段 + Unsafe CAS 实现; - 调用
get()、set()、compareAndSet()等方法时,JVM 自动插入对应内存屏障,满足 JMM 要求; - 自定义 CAS 逻辑(如通过
Unsafe)时,务必确保目标字段声明为volatile,否则行为未定义。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










