基础原子类
100 个线程同时对同一个变量执行 i++,最终结果一定是 100 吗?
如果你用 int i = 0,答案大概率是否——因为 i++ 不是原子操作。
但如果用 AtomicInteger i = new AtomicInteger(0),答案是肯定的。
这就是原子类的作用。
原子类家族
Java 并发包 java.util.concurrent.atomic 提供了完整的原子类家族:
java.util.concurrent.atomic
├── 基础类型
│ ├── AtomicInteger int 类型的原子操作
│ ├── AtomicLong long 类型的原子操作
│ └── AtomicBoolean boolean 类型的原子操作
│
├── 引用类型
│ ├── AtomicReference<V> 普通引用
│ ├── AtomicStampedReference<V> 带版本号的引用(解决 ABA)
│ └── AtomicMarkableReference<V> 带标记的引用
│
├── 数组类型
│ ├── AtomicIntegerArray
│ ├── AtomicLongArray
│ └── AtomicReferenceArray<V>
│
└── 字段更新器
├── AtomicIntegerFieldUpdater<T>
├── AtomicLongFieldUpdater<T>
└── AtomicReferenceFieldUpdater<T,V>常用方法一览
| 方法 | 说明 |
|---|---|
get() | 获取当前值 |
set(value) | 设置值 |
lazySet(value) | 延迟设置值(其他线程可能暂时读到旧值) |
getAndSet(value) | 原子地设置新值,返回旧值 |
compareAndSet(expected, update) | CAS 操作 |
incrementAndGet() | 递增并返回新值 |
getAndIncrement() | 返回旧值后再递增 |
decrementAndGet() | 递减并返回新值 |
getAndDecrement() | 返回旧值后再递减 |
addAndGet(delta) | 增加 delta 并返回新值 |
getAndAdd(delta) | 返回旧值后再增加 |
注意带 "getAnd" 前缀的方法都是返回旧值,不带前缀的返回新值。
代码演示
AtomicInteger 基础操作
java
public class AtomicIntegerDemo {
private static final AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) {
System.out.println("初始值: " + counter.get()); // 0
counter.set(10);
System.out.println("set(10): " + counter.get()); // 10
counter.incrementAndGet();
System.out.println("incrementAndGet(): " + counter.get()); // 11
counter.decrementAndGet();
System.out.println("decrementAndGet(): " + counter.get()); // 10
counter.addAndGet(5);
System.out.println("addAndGet(5): " + counter.get()); // 15
int old = counter.getAndAdd(3);
System.out.println("getAndAdd(3) 返回旧值: " + old); // 15
System.out.println("当前值: " + counter.get()); // 18
}
}CAS 操作
java
public class AtomicCASDemo {
private static final AtomicInteger value = new AtomicInteger(0);
public static void main(String[] args) {
// compareAndSet 成功情况
boolean success1 = value.compareAndSet(0, 10);
System.out.println("CAS(0->10): " + success1); // true
System.out.println("当前值: " + value.get()); // 10
// compareAndSet 失败情况
boolean success2 = value.compareAndSet(0, 20);
System.out.println("CAS(0->20): " + success2); // false
System.out.println("当前值: " + value.get()); // 仍然是 10
}
}无锁计数器
java
public class AtomicCounter {
private final AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
public int get() {
return count.get();
}
public static void main(String[] args) throws InterruptedException {
AtomicCounter counter = new AtomicCounter();
int threadCount = 100;
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
counter.increment();
}
}).start();
}
Thread.sleep(2000);
System.out.println("计数结果: " + counter.get()); // 应该是 100000
}
}AtomicLong 计时器
java
public class AtomicLongTimer {
private final AtomicLong startTime = new AtomicLong(0);
private final AtomicLong endTime = new AtomicLong(0);
public void start() {
startTime.set(System.currentTimeMillis());
}
public void end() {
endTime.set(System.currentTimeMillis());
}
public long duration() {
return endTime.get() - startTime.get();
}
public static void main(String[] args) {
AtomicLongTimer timer = new AtomicLongTimer();
timer.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
timer.end();
System.out.println("耗时: " + timer.duration() + "ms");
}
}AtomicBoolean 单次初始化
java
public class AtomicBooleanDemo {
private static final AtomicBoolean initialized = new AtomicBoolean(false);
public static void initialize() {
// 只有一个线程能成功初始化
if (initialized.compareAndSet(false, true)) {
System.out.println("初始化完成");
} else {
System.out.println("已经初始化过了");
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 5; i++) {
new Thread(() -> initialize(), "Thread-" + i).start();
}
Thread.sleep(1000);
}
}Lambda 更新函数
JDK 8+ 提供了更简洁的更新方式:
java
public class AtomicUpdateFunctions {
public static void main(String[] args) {
AtomicInteger ai = new AtomicInteger(10);
// updateAndGet: 更新后获取
int result1 = ai.updateAndGet(x -> x * 2);
System.out.println("updateAndGet(x*2): " + result1); // 20
// getAndUpdate: 获取后再更新
int result2 = ai.getAndUpdate(x -> x + 5);
System.out.println("getAndUpdate(x+5) 返回: " + result2); // 20
System.out.println("当前值: " + ai.get()); // 25
// accumulateAndGet: 累加
int result3 = ai.accumulateAndGet(3, Integer::sum);
System.out.println("accumulateAndGet(3, sum): " + result3); // 28
// getAndAccumulate: 获取后再累加
int result4 = ai.getAndAccumulate(2, Integer::sum);
System.out.println("getAndAccumulate(2, sum) 返回: " + result4); // 28
System.out.println("当前值: " + ai.get()); // 30
}
}注意事项
- 性能考虑:CAS 在高竞争下可能多次重试,极端情况下不如 synchronized
- ABA 问题:使用 AtomicStampedReference 解决(后面会详细讲)
- 复合操作:
map.get(k) + 1这种仍不是原子的,需要用循环 CAS - long/double:在 32 位 JVM 中,AtomicLong 使用 CAS 保证 64 位操作的原子性
选择指南
| 场景 | 推荐 |
|---|---|
| 简单计数 | AtomicInteger |
| 需要 long 范围 | AtomicLong |
| 布尔状态开关 | AtomicBoolean |
| 引用类型 | AtomicReference |
| 高并发累加 | LongAdder(见专门章节) |
