首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【Java面试】一文彻底搞懂 volatile + CAS:原子类到底在干什么?

【Java面试】一文彻底搞懂 volatile + CAS:原子类到底在干什么?

作者头像
予枫
发布2026-01-12 14:57:30
发布2026-01-12 14:57:30
1190
举报
文章被收录于专栏:Java 筑基与进阶Java 筑基与进阶

🍂 枫言枫语:我是予枫,一名行走在 Java 后端与多模态 AI 交叉路口的研二学生。 “予一人以深耕,观万木之成枫。” 在这里,我记录从底层源码到算法前沿的每一次思考。希望能与你一起,在逻辑的丛林中寻找技术的微光。

前言:为什么要写这篇?

在学习 Java 并发时,volatileCASAtomicInteger 几乎是必考内容,但很多人:

  • 会背: volatile 保证可见性 CAS 保证原子性
  • 一问就懵
    • 为什么一定要 volatile + CAS?
    • +1 是谁加的?
    • CAS 失败为什么要自旋?
    • CAS 到底“干了什么”?

这篇文章的目标只有一个:

用通俗但不肤浅的方式,把 volatile + CAS 一次讲透。


一、volatile + CAS 要解决什么问题?

1. 多线程的核心矛盾

多个线程同时访问并修改共享变量时,会遇到三个问题:

  1. 看不见:一个线程改了,另一个线程不知道
  2. 顺序乱:代码执行顺序和你写的不一样
  3. 一起改:多个线程同时修改,结果错误

Java 并发的本质,就是围绕这三个问题展开的。


二、volatile:负责“看得见 + 不乱序”

1. volatile 是什么?

volatile 是 Java 提供的一种轻量级并发关键字,它不加锁,但能提供部分并发安全保障。


2. volatile 能保证什么?

(1)可见性

代码语言:javascript
复制
volatile int x;
  • 一个线程写 x
  • 其他线程立刻能读到最新值

原因: volatile 会强制变量的读写发生在主内存,而不是线程私有缓存。


(2)禁止指令重排序(有序性)

代码语言:javascript
复制
value = 42;
flag = true; // volatile

只要另一个线程看到 flag == true, 就一定能看到 value == 42

这是因为 volatile 会在读写处插入内存屏障,建立 happens-before 关系。


3. volatile 不能保证什么?

不能保证原子性

代码语言:javascript
复制
volatile int count = 0;
count++;  // 非原子操作

count++ 本质是三步:

  1. 加 1

多线程下依然会丢失更新。


三、CAS:负责“安全地修改”

1. CAS 是什么?

CAS(Compare And Swap)是 CPU 硬件级支持的原子指令

它的逻辑是:

如果内存中的值 == 我期望的值 那就把它改成新值 否则,什么都不做


2. CAS 的伪代码
代码语言:javascript
复制
CAS(address, expected, newValue):
    if (*address == expected)
        *address = newValue
        return true
    else
        return false

✔ 比较 + 写入是一个不可分割的原子操作。


3. CAS 保证了什么?

原子性

但 CAS 本身:

  • 不保证可见性
  • 不保证执行顺序

四、为什么必须是 volatile + CAS?

这是理解 Atomic 类的关键。

1. 只用 volatile 会怎样?
  • 所有人都能看到最新值
  • 但多个线程仍可能同时修改 → 写覆盖

2. 只用 CAS 会怎样?
  • 修改是原子的
  • 但如果变量不是 volatile:
    • 可能读到旧值
    • 修改后别人也不一定立刻看到

3. AtomicInteger 的真实设计
代码语言:javascript
复制
public class AtomicInteger {
    private volatile int value;
}
  • volatile:保证读写可见、有序
  • CAS:保证更新原子

👉 两者缺一不可


五、+1 是谁定义的?

这是一个非常容易误解的点。

1. 结论先行

+1 完全是程序员定义的业务逻辑,不是 CAS 自带的能力。


2. 为什么大家都看到 +1?

因为最经典的并发问题是:

代码语言:javascript
复制
count++

JDK 提供了语义明确的方法:

代码语言:javascript
复制
atomicInt.incrementAndGet();

但其本质是:

代码语言:javascript
复制
int newValue = oldValue + 1; // 业务逻辑
CAS(oldValue, newValue);

CAS 并不关心你怎么算新值。


3. 非 +1 的 CAS 示例
  • 设置值
  • 状态切换
  • 任意累加
代码语言:javascript
复制
atomicInt.addAndGet(10);
state.compareAndSet(0, 1);

六、CAS 失败为什么要自旋?

1. CAS 是“乐观并发”

CAS 的设计假设是:

大多数时候不会冲突

失败说明:

“你刚才读到的值,已经被别人改了”


2. 为什么不阻塞?

阻塞意味着:

  • 线程挂起
  • 线程唤醒
  • 上下文切换

这些成本都远高于一次自旋重试


3. 自旋的本质
代码语言:javascript
复制
while (true) {
    int old = value;
    int newValue = old + 1;
    if (CAS(old, newValue)) {
        break;
    }
}

👉 自旋 = 重新读 + 重新算 + 再尝试


七、volatile + CAS + 自旋的完整配合

组件

作用

volatile

每次读到最新值

CAS

保证更新原子

自旋

保证语义最终成立

这是 Java 无锁并发的核心模型。


八、CAS 的局限性(必须知道)

1. ABA 问题

值从 A → B → A,CAS 看不出来。

解决方案:

  • AtomicStampedReference

2. 自旋开销
  • 高冲突下浪费 CPU

3. 只能操作一个变量

多变量一致性 → 仍需锁


九、总结一句话

volatile + CAS 是 Java 无锁并发的基石: volatile 解决“看得见、顺序对不对”, CAS 解决“改得安不安全”, 自旋保证在竞争下语义最终正确。

关于作者: 💡 予枫,某高校在读研究生,专注于 Java 后端开发与多模态情感计算。💬 欢迎点赞、收藏、评论,你的反馈是我持续输出的最大动力!

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻: https://cloud.tencent.com/developer/support-plan?invite_code=9wrxwtlju1l 当前加入还有惊喜相送!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:为什么要写这篇?
  • 一、volatile + CAS 要解决什么问题?
    • 1. 多线程的核心矛盾
  • 二、volatile:负责“看得见 + 不乱序”
    • 1. volatile 是什么?
    • 2. volatile 能保证什么?
    • 3. volatile 不能保证什么?
  • 三、CAS:负责“安全地修改”
    • 1. CAS 是什么?
    • 2. CAS 的伪代码
    • 3. CAS 保证了什么?
  • 四、为什么必须是 volatile + CAS?
    • 1. 只用 volatile 会怎样?
    • 2. 只用 CAS 会怎样?
    • 3. AtomicInteger 的真实设计
  • 五、+1 是谁定义的?
    • 1. 结论先行
    • 2. 为什么大家都看到 +1?
    • 3. 非 +1 的 CAS 示例
  • 六、CAS 失败为什么要自旋?
    • 1. CAS 是“乐观并发”
    • 2. 为什么不阻塞?
    • 3. 自旋的本质
  • 七、volatile + CAS + 自旋的完整配合
  • 八、CAS 的局限性(必须知道)
    • 1. ABA 问题
    • 2. 自旋开销
    • 3. 只能操作一个变量
  • 九、总结一句话
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档