原子操作类Atomic
原子类位于 java.util.concurrent.atomic 包,用于在无锁或低锁开销下完成单变量原子更新。它们的核心基础是 CAS。
# 1. CAS
CAS:Compare And Swap,比较并交换。
内存值 V
预期值 A
新值 B
如果 V == A:
V = B,成功
否则:
失败,重试或返回
示例:
AtomicInteger count = new AtomicInteger();
count.incrementAndGet();
概念流程:
读取旧值 old
计算新值 new = old + 1
CAS(old, new)
├─ 成功:返回
└─ 失败:重新读取并重试
# 2. 常见原子类
| 类型 | 示例 |
|---|---|
| 基本类型 | AtomicInteger、AtomicLong、AtomicBoolean |
| 引用类型 | AtomicReference |
| 数组 | AtomicIntegerArray、AtomicReferenceArray |
| 字段更新器 | AtomicIntegerFieldUpdater |
| 高并发累加 | LongAdder、LongAccumulator |
# 3. AtomicInteger
AtomicInteger value = new AtomicInteger(0);
value.incrementAndGet();
value.getAndIncrement();
value.addAndGet(10);
value.compareAndSet(11, 20);
方法区别:
| 方法 | 返回 |
|---|---|
incrementAndGet | 增加后的值 |
getAndIncrement | 增加前的值 |
addAndGet | 增加后的值 |
compareAndSet | CAS 是否成功 |
# 4. AtomicReference
AtomicReference 用于原子更新对象引用。
AtomicReference<Config> ref = new AtomicReference<>(oldConfig);
ref.compareAndSet(oldConfig, newConfig);
适合:
- 配置整体替换。
- 不可变对象引用更新。
- 简单状态机。
如果对象内部可变,原子引用只能保证引用替换原子,不能保证对象内部线程安全。
# 5. ABA 问题
CAS 只比较当前值是否等于预期值,可能出现 ABA:
线程 A 读取值 A
线程 B 把 A 改成 B
线程 B 又把 B 改回 A
线程 A CAS 看到还是 A,误以为没变
解决:
AtomicStampedReference加版本号。AtomicMarkableReference加标记。
# 6. LongAdder
高并发计数时,AtomicLong 多线程竞争同一个变量,CAS 失败重试可能很多。
LongAdder 把热点分散到多个 Cell。
LongAdder
├─ base
├─ Cell[0]
├─ Cell[1]
└─ Cell[2]
sum = base + 所有 Cell
使用:
LongAdder adder = new LongAdder();
adder.increment();
long total = adder.sum();
适合高并发统计,不适合要求每次读取都严格实时一致的场景。
# 7. 原子类适用边界
适合:
- 单变量更新。
- 计数器。
- 状态标记。
- 引用整体替换。
不适合:
- 多个变量需要保持一致。
- 复杂业务临界区。
- 需要条件等待和通知。
例如:
if (stock.get() > 0) {
stock.decrementAndGet();
}
这仍可能有竞态。需要用 CAS 循环或锁保护整个逻辑。
# 8. CAS 与锁
| 对比项 | CAS | 锁 |
|---|---|---|
| 阻塞 | 不阻塞 | 竞争时可能阻塞 |
| 适合 | 短小单变量操作 | 复杂临界区 |
| 高竞争 | 可能大量重试 | 可能阻塞等待 |
| 可读性 | 复杂逻辑可读性差 | 业务临界区更清晰 |
不要为了“无锁”牺牲正确性和可维护性。
# 专家实践与边界
Atomic 类适合单变量原子更新,它的核心通常是 CAS。CAS 避免了阻塞锁,但在高竞争下会反复重试,导致 CPU 消耗上升。
读取旧值
│
▼
计算新值
│
▼
CAS(旧值, 新值)
├─ 成功:完成更新
└─ 失败:重新读取并重试
| 场景 | 推荐 | 原因 |
|---|---|---|
| 单个计数器 | AtomicLong | 简单直接 |
| 高并发统计 | LongAdder | 分散热点竞争 |
| 原子引用替换 | AtomicReference | 配置快照、状态机 |
| 多字段一致性 | 锁或不可变对象 | Atomic 只能保护单点 |
| 乐观更新 | CAS + 版本 | 需要处理失败重试 |
不要把 Atomic 当作“无锁万能工具”。当业务要求“检查余额并扣减库存”这类复合不变量时,更清晰的锁或事务往往更正确。
# Tips 快问快答
Q:Atomic 的核心是什么? A:CAS,加上 volatile 可见性语义。
Q:CAS 是什么? A:比较当前值是否等于预期值,相等才更新为新值。
Q:AtomicInteger 能替代所有锁吗? A:不能,只适合单变量原子更新。
Q:ABA 问题是什么? A:值从 A 变 B 又变 A,CAS 无法感知中间变化。
Q:LongAdder 为什么高并发更快? A:它把竞争分散到多个 Cell,减少同一变量 CAS 冲突。
Q:LongAdder 适合精确实时读取吗? A:不完全适合,它更适合统计累加。
Q:AtomicReference 保证对象内部线程安全吗? A:不保证,只保证引用更新原子。
Q:CAS 失败会怎样? A:通常循环重试,竞争激烈时 CPU 消耗会上升。