【JUC】原子类
你这段对 原子类 的理解整体是对的,核心就是:
原子类 = JUC 提供的一组基于 CAS 的线程安全变量操作工具,适合做简单共享变量的原子更新。
我帮你把几个重点再理顺一下。
1. 原子类解决什么问题?
比如普通的:
count++;
它不是原子操作,本质上大概是三步:
读取 count
count + 1
写回 count
多线程同时执行时,可能两个线程都读到同一个旧值,导致更新丢失。
所以我们需要保证:
读取 -> 修改 -> 写回
这整个过程不能被其他线程破坏。
原子类就是为了解决这种简单变量的并发更新问题。
2. AtomicInteger 为什么线程安全?
以:
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet();
为例,它底层不是简单地 count++,而是类似这样:
do {
oldValue = 当前值;
newValue = oldValue + 1;
} while (!CAS(oldValue, newValue));
意思是:
- 先读取旧值,比如
oldValue = 0 - 计算新值,比如
newValue = 1 - 用 CAS 尝试更新:
- 如果当前值还是 0,说明没人改过,更新成 1
- 如果当前值不是 0,说明被其他线程改过,更新失败,重新来一遍
所以 AtomicInteger 的递增不是一次普通操作,而是 循环 CAS 重试。
3. CAS 怎么保证原子性?
CAS 的全称是:
Compare And Swap
比较并交换
它需要三个值:
内存地址 V
预期值 A
新值 B
逻辑是:
if (V 当前值 == A) {
V = B;
return true;
} else {
return false;
}
关键点是:
这个“比较 + 更新”在 CPU 层面是一条原子指令完成的,不会被线程切换打断。
所以 CAS 本身是原子的。
普通 Java 代码里的:
if (value == expected) {
value = newValue;
}
不是原子的。
但是 CPU 提供的 CAS 指令可以保证这两个动作作为一个整体完成。
4. 为什么说原子类是无锁?
因为它没有用:
synchronized
Lock
也就是说,线程更新失败后不会进入阻塞状态,而是继续自旋重试。
比如线程 A 和线程 B 同时执行 incrementAndGet():
初始值:
count = 0
线程 A 读到 0,准备改成 1。
线程 B 也读到 0,准备改成 1。
线程 A 先 CAS 成功:
count: 0 -> 1
线程 B 再 CAS 时发现:
预期值是 0
但当前值已经是 1
所以线程 B CAS 失败,然后重新读取:
oldValue = 1
newValue = 2
再次 CAS,成功:
count: 1 -> 2
最终结果正确。
5. 原子类适合什么场景?
你总结得很好,适合:
简单变量的线程安全更新
例如:
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet();
常见场景:
请求次数统计
库存扣减
在线人数统计
序号生成
状态标记
引用对象的原子替换
但是要注意,它更适合 单个变量的原子操作。
比如:
AtomicInteger count;
这种很合适。
6. 原子类不适合什么场景?
如果你要保证多个操作整体一致,比如:
if (stock > 0) {
stock--;
createOrder();
}
这就不只是一个简单变量自增/自减了,而是多个步骤组成的业务逻辑。
这种情况下,单纯 AtomicInteger 不一定够,需要考虑:
synchronized
Lock
数据库事务
分布式锁
因为原子类只能保证某一次变量更新是原子的,不能天然保证一整段业务代码是原子的。
7. 你这段可以优化成面试版
可以这样说:
Java 原子类是
java.util.concurrent.atomic包下的一组并发工具类,比如AtomicInteger、AtomicLong、AtomicReference等。它们主要用于在多线程环境下对共享变量进行线程安全的原子更新。原子类的核心原理是 CAS,也就是 Compare-And-Swap。CAS 会比较当前内存值和预期值是否相等,如果相等就更新为新值,否则更新失败并重试。这个比较和更新操作依赖 CPU 的原子指令完成,因此本身具有原子性。
以
AtomicInteger.incrementAndGet()为例,它底层并不是普通的i++,而是通过 Unsafe 类调用底层 CAS 操作,采用循环重试的方式完成自增。如果 CAS 成功就返回新值,如果失败就重新读取最新值再尝试更新。原子类属于无锁方案,不会像 synchronized 那样阻塞线程,因此在简单计数、自增、自减、状态更新等场景下性能较好。但它更适合单个变量的原子操作,如果是多个变量或一整段业务逻辑需要保持一致,仍然需要使用 synchronized、Lock 或事务等机制。
你这里最关键要记住一句话:
原子类不是让
i++变成原子,而是底层用 CAS 循环重新实现了一个线程安全的i++。
CAS 的“比较 + 更新”由 CPU 原子指令保证不可分割,所以整个自增操作最终是线程安全的。
AtomicInteger.incrementAndGet()底层调用 Unsafe 的getAndAddInt方法,它内部通过循环 CAS 实现。每次先读取 volatile 修饰的 value 值,计算出新值,然后调用 CAS 判断当前内存值是否仍然等于旧值。如果相等就更新成功,否则说明被其他线程修改过,就重新读取并重试。CAS 底层依赖 CPU 的原子指令,比如 cmpxchg,保证比较和更新这一步是原子的,因此可以实现无锁线程安全自增。
更多推荐




所有评论(0)