🍂 枫言枫语:我是予枫,一名行走在 Java 后端与多模态 AI 交叉路口的研二学生。 “予一人以深耕,观万木之成枫。” 在这里,我记录从底层源码到算法前沿的每一次思考。希望能与你一起,在逻辑的丛林中寻找技术的微光。
在学习 Java 并发时,volatile、CAS、AtomicInteger 几乎是必考内容,但很多人:
+1 是谁加的?
这篇文章的目标只有一个:
用通俗但不肤浅的方式,把 volatile + CAS 一次讲透。
多个线程同时访问并修改共享变量时,会遇到三个问题:
Java 并发的本质,就是围绕这三个问题展开的。
volatile 是 Java 提供的一种轻量级并发关键字,它不加锁,但能提供部分并发安全保障。
(1)可见性
volatile int x;原因: volatile 会强制变量的读写发生在主内存,而不是线程私有缓存。
(2)禁止指令重排序(有序性)
value = 42;
flag = true; // volatile只要另一个线程看到 flag == true,
就一定能看到 value == 42。
这是因为 volatile 会在读写处插入内存屏障,建立 happens-before 关系。
❌ 不能保证原子性
volatile int count = 0;
count++; // 非原子操作count++ 本质是三步:
多线程下依然会丢失更新。
CAS(Compare And Swap)是 CPU 硬件级支持的原子指令。
它的逻辑是:
如果内存中的值 == 我期望的值 那就把它改成新值 否则,什么都不做
CAS(address, expected, newValue):
if (*address == expected)
*address = newValue
return true
else
return false✔ 比较 + 写入是一个不可分割的原子操作。
✅ 原子性
但 CAS 本身:
这是理解 Atomic 类的关键。
public class AtomicInteger {
private volatile int value;
}volatile:保证读写可见、有序
CAS:保证更新原子
👉 两者缺一不可
+1 是谁定义的?这是一个非常容易误解的点。
+1完全是程序员定义的业务逻辑,不是 CAS 自带的能力。
因为最经典的并发问题是:
count++JDK 提供了语义明确的方法:
atomicInt.incrementAndGet();但其本质是:
int newValue = oldValue + 1; // 业务逻辑
CAS(oldValue, newValue);CAS 并不关心你怎么算新值。
atomicInt.addAndGet(10);
state.compareAndSet(0, 1);CAS 的设计假设是:
大多数时候不会冲突
失败说明:
“你刚才读到的值,已经被别人改了”
阻塞意味着:
这些成本都远高于一次自旋重试。
while (true) {
int old = value;
int newValue = old + 1;
if (CAS(old, newValue)) {
break;
}
}👉 自旋 = 重新读 + 重新算 + 再尝试
组件 | 作用 |
|---|---|
volatile | 每次读到最新值 |
CAS | 保证更新原子 |
自旋 | 保证语义最终成立 |
这是 Java 无锁并发的核心模型。
值从 A → B → A,CAS 看不出来。
解决方案:
AtomicStampedReference
多变量一致性 → 仍需锁
volatile + CAS 是 Java 无锁并发的基石: volatile 解决“看得见、顺序对不对”, CAS 解决“改得安不安全”, 自旋保证在竞争下语义最终正确。
关于作者: 💡 予枫,某高校在读研究生,专注于 Java 后端开发与多模态情感计算。💬 欢迎点赞、收藏、评论,你的反馈是我持续输出的最大动力!
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻: https://cloud.tencent.com/developer/support-plan?invite_code=9wrxwtlju1l 当前加入还有惊喜相送!