原子类与CAS (Atomic Classes & CAS)¶
深入理解Atomic类、CAS(Compare-And-Swap)原理、ABA问题及解决方案
目录¶
- 1. 为什么需要原子类
- 2. CAS原理
- 3. AtomicInteger详解
- 4. 其他原子类
- 5. ABA问题
- 6. LongAdder vs AtomicLong
- 7. 最佳实践
- 8. 面试高频问题
1. 为什么需要原子类 (Why Atomic Classes?)¶
1.1 传统同步的问题¶
/**
* 传统同步方式的问题
* Problems with Traditional Synchronization
*/
public class TraditionalSync {
private int count = 0;
// 方式1:使用synchronized(性能较低)
public synchronized void increment() {
count++;
}
// 方式2:使用volatile(不保证原子性)
private volatile int count2 = 0;
public void increment2() {
count2++; // ❌ 不是原子操作!
}
}
问题:
- synchronized:性能较低,需要加锁
- volatile:只保证可见性,不保证原子性
1.2 原子类的优势¶
**原子类(Atomic Classes)**基于CAS实现,提供无锁的线程安全操作:
- 无锁 - 基于CAS,性能高
- 原子性 - 保证操作的原子性
- 高性能 - 比synchronized性能更好
1.3 在算力平台中的应用¶
在算力平台的结算系统中,需要原子操作保证线程安全:
/**
* 算力平台中的原子类应用
* Atomic Classes in Computing Platform
*/
public class PlatformAtomic {
// 场景1:任务计数器(原子操作)
private AtomicInteger taskCount = new AtomicInteger(0);
public void incrementTaskCount() {
taskCount.incrementAndGet(); // 原子递增
}
// 场景2:用户钱包余额(原子操作)
private ConcurrentHashMap<Long, AtomicLong> walletMap = new ConcurrentHashMap<>();
public boolean deduct(Long userId, Long amount) {
AtomicLong balance = walletMap.computeIfAbsent(
userId, k -> new AtomicLong(0)
);
// 原子更新:CAS操作
return balance.updateAndGet(current -> {
if (current >= amount) {
return current - amount;
}
throw new InsufficientBalanceException();
}) >= 0;
}
// 场景3:统计信息(原子操作)
private AtomicLong totalRevenue = new AtomicLong(0);
public void addRevenue(Long amount) {
totalRevenue.addAndGet(amount); // 原子累加
}
}
2. CAS原理 (CAS Principle)¶
2.1 什么是CAS?¶
**CAS(Compare-And-Swap,比较并交换)**是一种无锁算法:
2.2 CAS操作流程¶
/**
* CAS操作伪代码
* CAS Operation Pseudocode
*/
public boolean compareAndSwap(int memoryValue, int expectedValue, int newValue) {
if (memoryValue == expectedValue) {
memoryValue = newValue;
return true; // 更新成功
}
return false; // 更新失败
}
实际执行: 1. 读取内存值V 2. 比较V和预期值A 3. 如果相等,更新为B;否则重试
2.3 CAS的原子性保证¶
CAS操作在CPU层面是原子的,通过**CPU指令**实现:
- x86架构:
CMPXCHG指令 - ARM架构:
LDREX/STREX指令对
2.4 CAS的优缺点¶
优点: - 无锁 - 不需要加锁,性能高 - 原子性 - CPU指令保证原子性 - 无阻塞 - 不会导致线程阻塞
缺点: - ABA问题 - 值可能被其他线程修改后又改回原值 - 自旋开销 - 如果竞争激烈,可能长时间自旋 - 只能保证一个变量 - 不能保证多个变量的原子性
3. AtomicInteger详解 (AtomicInteger Details)¶
3.1 核心方法¶
import java.util.concurrent.atomic.AtomicInteger;
/**
* AtomicInteger核心方法
* AtomicInteger Core Methods
*/
public class AtomicIntegerDemo {
private AtomicInteger count = new AtomicInteger(0);
// 基本操作
public void basicOperations() {
// 获取值
int value = count.get();
// 设置值
count.set(10);
// 获取并设置
int oldValue = count.getAndSet(20);
// 比较并设置(CAS)
boolean success = count.compareAndSet(20, 30);
}
// 递增递减操作
public void incrementDecrement() {
// 递增并返回新值
int newValue = count.incrementAndGet();
// 返回旧值并递增
int oldValue = count.getAndIncrement();
// 递减并返回新值
int newValue2 = count.decrementAndGet();
// 返回旧值并递减
int oldValue2 = count.getAndDecrement();
}
// 加减操作
public void addSubtract() {
// 加指定值并返回新值
int newValue = count.addAndGet(5);
// 返回旧值并加指定值
int oldValue = count.getAndAdd(5);
}
// 更新操作
public void update() {
// 原子更新:使用函数式接口
count.updateAndGet(x -> x * 2);
// 获取并更新
int oldValue = count.getAndUpdate(x -> x + 10);
}
}
3.2 源码分析¶
/**
* AtomicInteger核心实现(简化版)
* Core Implementation (Simplified)
*/
public class AtomicInteger {
private volatile int value;
// CAS操作:使用Unsafe类
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
// 递增操作
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
// getAndAddInt实现(自旋)
public final int getAndAddInt(Object obj, long offset, int delta) {
int v;
do {
v = getIntVolatile(obj, offset); // 获取当前值
} while (!compareAndSwapInt(obj, offset, v, v + delta)); // CAS直到成功
return v;
}
}
关键点:
- 使用volatile保证可见性
- 使用Unsafe.compareAndSwapInt()实现CAS
- 自旋直到CAS成功
4. 其他原子类 (Other Atomic Classes)¶
4.1 AtomicLong¶
import java.util.concurrent.atomic.AtomicLong;
/**
* AtomicLong使用示例
* AtomicLong Usage Example
*/
public class AtomicLongDemo {
private AtomicLong counter = new AtomicLong(0);
public void increment() {
counter.incrementAndGet();
}
public long get() {
return counter.get();
}
}
4.2 AtomicBoolean¶
import java.util.concurrent.atomic.AtomicBoolean;
/**
* AtomicBoolean使用示例
* AtomicBoolean Usage Example
*/
public class AtomicBooleanDemo {
private AtomicBoolean flag = new AtomicBoolean(false);
public boolean trySet() {
// 只有当前值为false时才设置为true
return flag.compareAndSet(false, true);
}
}
4.3 AtomicReference¶
import java.util.concurrent.atomic.AtomicReference;
/**
* AtomicReference使用示例
* AtomicReference Usage Example
*/
public class AtomicReferenceDemo {
private AtomicReference<String> ref = new AtomicReference<>("初始值");
public void update() {
// 原子更新引用
ref.compareAndSet("初始值", "新值");
// 使用函数式更新
ref.updateAndGet(s -> s + "追加");
}
}
4.4 原子数组类¶
import java.util.concurrent.atomic.AtomicIntegerArray;
/**
* AtomicIntegerArray使用示例
* AtomicIntegerArray Usage Example
*/
public class AtomicIntegerArrayDemo {
private AtomicIntegerArray array = new AtomicIntegerArray(10);
public void update(int index, int value) {
array.set(index, value);
array.compareAndSet(index, value, value + 1);
}
}
4.5 原子更新字段类¶
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* AtomicIntegerFieldUpdater使用示例
* AtomicIntegerFieldUpdater Usage Example
*/
public class AtomicIntegerFieldUpdaterDemo {
static class Counter {
volatile int count = 0; // 必须是volatile
}
private static final AtomicIntegerFieldUpdater<Counter> updater =
AtomicIntegerFieldUpdater.newUpdater(Counter.class, "count");
public void update(Counter counter) {
updater.incrementAndGet(counter);
}
}
5. ABA问题 (ABA Problem)¶
5.1 什么是ABA问题?¶
ABA问题:CAS操作时,值从A变为B,再变回A,CAS认为值没有变化,但实际上已经被修改过。
5.2 ABA问题示例¶
/**
* ABA问题示例
* ABA Problem Example
*/
public class ABAProblem {
private AtomicReference<String> ref = new AtomicReference<>("A");
public void demonstrateABA() {
// 线程1:A -> B -> A
new Thread(() -> {
ref.compareAndSet("A", "B");
ref.compareAndSet("B", "A");
}).start();
// 线程2:检查值是否为A,如果是则更新为C
new Thread(() -> {
try {
Thread.sleep(100); // 等待线程1完成
} catch (InterruptedException e) {
e.printStackTrace();
}
// 此时值已经是A(但中间被修改过),CAS成功
boolean success = ref.compareAndSet("A", "C");
System.out.println("更新成功: " + success); // true
}).start();
}
}
5.3 解决方案:AtomicStampedReference¶
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* 使用版本号解决ABA问题
* Solve ABA Problem with Version Number
*/
public class ABAProblemSolution {
private AtomicStampedReference<String> ref =
new AtomicStampedReference<>("A", 1); // 初始值A,版本号1
public void solveABA() {
// 线程1:A(1) -> B(2) -> A(3)
new Thread(() -> {
int[] stampHolder = new int[1];
String value = ref.get(stampHolder);
ref.compareAndSet(value, "B", stampHolder[0], stampHolder[0] + 1);
value = ref.get(stampHolder);
ref.compareAndSet(value, "A", stampHolder[0], stampHolder[0] + 1);
}).start();
// 线程2:检查值和版本号
new Thread(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
int[] stampHolder = new int[1];
String value = ref.get(stampHolder);
// 版本号不匹配,更新失败
boolean success = ref.compareAndSet("A", "C", 1, 2);
System.out.println("更新成功: " + success); // false
}).start();
}
}
5.4 解决方案:AtomicMarkableReference¶
import java.util.concurrent.atomic.AtomicMarkableReference;
/**
* 使用标记解决ABA问题
* Solve ABA Problem with Mark
*/
public class ABAProblemSolution2 {
private AtomicMarkableReference<String> ref =
new AtomicMarkableReference<>("A", false);
public void solveABA() {
// 使用boolean标记代替版本号
boolean[] markHolder = new boolean[1];
String value = ref.get(markHolder);
ref.compareAndSet(value, "B", false, true);
}
}
6. LongAdder vs AtomicLong (LongAdder vs AtomicLong)¶
6.1 性能对比¶
在高并发场景下,LongAdder性能优于AtomicLong:
| 特性 | AtomicLong | LongAdder |
|---|---|---|
| 实现 | 单个volatile变量 | 分段累加(Cell数组) |
| 低并发 | 性能相当 | 性能相当 |
| 高并发 | 性能下降(CAS竞争) | 性能更好(分散竞争) |
| 内存 | 占用少 | 占用多(Cell数组) |
| 精度 | 精确 | 最终一致(读取时累加) |
6.2 LongAdder原理¶
import java.util.concurrent.atomic.LongAdder;
/**
* LongAdder原理:分段累加
* LongAdder Principle: Segmented Accumulation
*/
public class LongAdderDemo {
private LongAdder adder = new LongAdder();
public void increment() {
adder.increment(); // 分散到不同的Cell
}
public long sum() {
return adder.sum(); // 累加所有Cell的值
}
}
原理:
- 内部维护一个Cell数组
- 每个线程累加到不同的Cell
- 读取时累加所有Cell的值
- 减少CAS竞争,提高性能
6.3 使用建议¶
/**
* 使用建议
* Usage Recommendations
*/
public class AtomicChoice {
// 低并发场景:使用AtomicLong
private AtomicLong lowConcurrencyCounter = new AtomicLong(0);
// 高并发场景:使用LongAdder
private LongAdder highConcurrencyCounter = new LongAdder();
// 需要精确值:使用AtomicLong
private AtomicLong preciseCounter = new AtomicLong(0);
// 可以容忍最终一致:使用LongAdder
private LongAdder eventualConsistentCounter = new LongAdder();
}
7. 最佳实践 (Best Practices)¶
7.1 选择合适的原子类¶
// 简单计数器:AtomicInteger
AtomicInteger counter = new AtomicInteger(0);
// 高并发计数器:LongAdder
LongAdder highConcurrencyCounter = new LongAdder();
// 引用类型:AtomicReference
AtomicReference<String> ref = new AtomicReference<>();
// 需要版本号:AtomicStampedReference
AtomicStampedReference<String> stampedRef =
new AtomicStampedReference<>("value", 1);
7.2 避免ABA问题¶
// ✅ 使用版本号
AtomicStampedReference<String> ref =
new AtomicStampedReference<>("A", 1);
// ✅ 使用标记
AtomicMarkableReference<String> ref =
new AtomicMarkableReference<>("A", false);
7.3 合理使用LongAdder¶
// 高并发累加:使用LongAdder
LongAdder totalRevenue = new LongAdder();
for (int i = 0; i < 1000; i++) {
executorService.submit(() -> {
totalRevenue.add(100);
});
}
long sum = totalRevenue.sum(); // 最终累加值
8. 面试高频问题 (Interview Questions)¶
Q1: CAS的原理是什么?¶
答案: Compare-And-Swap,比较内存值和预期值,如果相等则更新为新值,否则重试。通过CPU指令保证原子性。
Q2: CAS的优缺点?¶
答案: - 优点:无锁、高性能、无阻塞 - 缺点:ABA问题、自旋开销、只能保证一个变量
Q3: 什么是ABA问题?如何解决?¶
答案: 值从A变为B再变回A,CAS认为值没变化。使用AtomicStampedReference(版本号)或AtomicMarkableReference(标记)解决。
Q4: LongAdder和AtomicLong的区别?¶
答案: LongAdder使用分段累加,高并发性能更好;AtomicLong使用单个变量,低并发性能相当。
Q5: 什么时候使用原子类?¶
答案: 简单的原子操作(如计数器),需要高性能的场景,可以替代synchronized的场景。
📖 扩展阅读¶
返回: 07-Java并发编程
上一章: 07-06 - 并发集合
下一章: 07-08 - 并发设计模式 →