将通过一个"银行金库多保险箱系统"的故事,生动解释Atomic类的实现原理。想象一个现代化银行的金库,里面有多个保险箱,每个保险箱都有独特的保护机制,确保多个金库管理员同时操作时不会出错。
故事设定:银行金库系统
- 🏦 银行金库:内存中的共享数据
- 🔒 保险箱:AtomicInteger等原子类
- 👨💼 金库管理员:操作数据的线程
- 📝 操作指令:CAS(Compare And Swap)操作
- 🔄 重试机制:自旋操作
- 🔬 显微镜:Unsafe类直接操作内存
普通变量的危险操作
java
Copy
public class UnsafeBank {
private int balance = 0; // 普通变量
public void deposit(int amount) {
// 三个步骤:读取→修改→写入
balance = balance + amount;
}
public static void main(String[] args) throws InterruptedException {
UnsafeBank bank = new UnsafeBank();
// 10个管理员同时存款
List<Thread> admins = new ArrayList<>();
for (int i = 0; i < 10; i++) {
admins.add(new Thread(() -> {
for (int j = 0; j < 1000; j++) {
bank.deposit(1);
}
}));
}
admins.forEach(Thread::start);
for (Thread admin : admins) {
admin.join();
}
System.out.println("最终余额: " + bank.balance);
// 预期是10000,但实际可能小于10000
}
}
问题分析:
管理员A读取余额为100,管理员B也读取到100
管理员A计算100+1=101并写入
管理员B计算100+1=101并写入
实际只增加1元,丢失1次存款!
Atomic保险箱解决方案
java
Copy
import java.util.concurrent.atomic.AtomicInteger;
public class SafeBank {
// 原子保险箱
private AtomicInteger balance = new AtomicInteger(0);
public void deposit(int amount) {
// 使用原子操作
balance.addAndGet(amount);
}
public static void main(String[] args) throws InterruptedException {
SafeBank bank = new SafeBank();
// 10个管理员同时存款
List<Thread> admins = new ArrayList<>();
for (int i = 0; i < 10; i++) {
admins.add(new Thread(() -> {
for (int j = 0; j < 1000; j++) {
bank.deposit(1);
}
}));
}
admins.forEach(Thread::start);
for (Thread admin : admins) {
admin.join();
}
System.out.println("最终余额: " + bank.balance.get());
// 始终是10000
}
}
核心原理:CAS操作(比较并交换)
CAS操作流程(保险箱操作员的工作流程)
-
读取当前值:查看保险箱当前余额(V = current)
-
计算新值:计算存款后的余额(newValue = V + amount)
-
尝试更新:
-
再次查看保险箱当前值(currentNow)
-
如果 currentNow == V(期间无人动过)
- 则更新为 newValue
- 返回成功
-
否则:重新开始步骤1(自旋)
-
Java源码实现(AtomicInteger.addAndGet)
java
Copy
public class AtomicInteger extends Number {
private volatile int value; // 关键:volatile保证可见性
public final int addAndGet(int delta) {
// 自旋操作(可能多次尝试)
return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
}
Unsafe类的实现:
java
Copy
public final int getAndAddInt(Object o, long offset, int delta) {
int current;
do {
// 步骤1:读取当前值
current = this.getIntVolatile(o, offset);
// 步骤2:尝试CAS更新(当前值=current,新值=current+delta)
// 步骤3:如果失败则自旋重试
} while (!this.compareAndSwapInt(o, offset, current, current + delta));
return current;
}
CAS底层:CPU指令支持
CAS操作最终映射到一条CPU指令:
- x86架构:
CMPXCHG指令 - ARM架构:
LDREX/STREX指令对
assembly
Copy
; x86汇编示例
mov eax, oldValue ; 加载期望值
mov ebx, newValue ; 加载新值
lock cmpxchg [mem], ebx ; 原子比较交换
Atomic类家族
1. 基本类型
AtomicBoolean:布尔值原子类AtomicInteger:整型原子类AtomicLong:长整型原子类
2. 引用类型
AtomicReference:对象引用原子类AtomicStampedReference:带版本号的对象引用(解决ABA问题)AtomicMarkableReference:带标记位的对象引用
3. 数组类型
AtomicIntegerArray:整型数组原子类AtomicLongArray:长整型数组原子类AtomicReferenceArray:引用数组原子类
4. 字段更新器
AtomicIntegerFieldUpdater:整型字段原子更新器AtomicLongFieldUpdater:长整型字段原子更新器AtomicReferenceFieldUpdater:引用字段原子更新器
解决ABA问题:带版本号的保险箱
ABA问题示例:
-
管理员A看到保险箱余额是100
-
管理员B取走100(余额=0)
-
管理员C存入100(余额=100)
-
管理员A的CAS操作:预期100→200,成功!
- 但实际上中间发生了B和C的操作
解决方案:AtomicStampedReference
java
Copy
public class ABASolution {
static AtomicStampedReference<Integer> balance =
new AtomicStampedReference<>(100, 0); // 初始值100,版本0
public static void main(String[] args) {
// 管理员A尝试修改(记录初始版本)
int oldStamp = balance.getStamp();
Integer oldValue = balance.getReference();
// 模拟管理员B取款100
balance.compareAndSet(100, 0, 0, 1);
// 模拟管理员C存款100
balance.compareAndSet(0, 100, 1, 2);
// 管理员A尝试存款100
boolean success = balance.compareAndSet(
oldValue,
oldValue + 100,
oldStamp, // 预期版本0
oldStamp + 1);
System.out.println("管理员A操作成功: " + success); // false
}
}
性能优势:无锁算法
与锁的性能对比
| 操作类型 | 锁(synchronized) | Atomic CAS |
|---|---|---|
| 低竞争 | 100 ns | 20 ns |
| 中等竞争 | 500 ns | 30 ns |
| 高竞争 | 1000+ ns | 100+ ns |
| 内存开销 | 对象头+监视器 | 单个变量 |
适用场景
-
推荐CAS:
- 计数器、状态标志
- 集合中的并发更新
- 非阻塞算法
-
推荐锁:
- 复杂数据结构的保护
- 需要等待条件满足的操作
- 事务性操作
高级应用:自定义原子操作
使用AtomicIntegerFieldUpdater示例:
java
Copy
public class Account {
private volatile int balance; // 必须是volatile
private static final AtomicIntegerFieldUpdater<Account> updater =
AtomicIntegerFieldUpdater.newUpdater(Account.class, "balance");
public void deposit(int amount) {
updater.addAndGet(this, amount);
}
}
实现原理总结
-
volatile基础:
- 所有Atomic类内部都使用volatile变量保证可见性
- 写操作后强制刷新到主内存
- 读操作前强制从主内存读取
-
CAS核心:
- 自旋重试机制
- 底层CPU指令支持
- 比较当前值与预期值
- 相同则更新,不同则重试
-
解决ABA问题:
- 使用版本号或标记位
AtomicStampedReferenceAtomicMarkableReference
-
无锁算法优势:
- 避免线程阻塞
- 减少上下文切换
- 提高吞吐量
正如我们的银行金库系统,Atomic类就像每个保险箱都配备了独立的操作员,通过高效的协调机制(CAS),让多个管理员能安全、高效地同时工作,而无需排队等待大金库门锁(synchronized)!
Atomic类是构建高性能并发工具的基础,在并发包中的ConcurrentHashMap、LongAdder等类中都广泛使用。理解其原理是掌握Java高并发的关键一步。