Java 的 java.util.concurrent.atomic 包提供了一系列 原子类,基于 CAS(Compare-and-Swap)操作实现无锁线程安全,避免了显式锁的开销。除了常用的 AtomicInteger,原子类可按功能分为 6 大类,覆盖数值操作、引用操作、数组、字段更新等场景,以下是完整分类与核心用法:
一、基本类型原子类(替换基础类型的线程安全版本)
核心是对 int、long、boolean、double 等基本类型提供原子的增减、赋值、比较交换操作,底层直接操作内存地址,性能优于锁。
| 原子类 | 功能说明 | 核心方法示例 |
|---|---|---|
AtomicLong | 线程安全的 long 类型,支持增减、累加、CAS 操作(适用于大数值计数) | incrementAndGet()(自增 1 并返回)、addAndGet(10)(加 10)、compareAndSet(旧值, 新值) |
AtomicBoolean | 线程安全的 boolean 类型,常用于状态标记(如 “是否初始化完成”) | compareAndSet(false, true)(CAS 修改状态)、getAndSet(true)(获取旧值并设新值) |
AtomicByte | 线程安全的 byte 类型(8 位整数) | incrementAndGet()、decrementAndGet()、getAndAdd(5) |
AtomicShort | 线程安全的 short 类型(16 位整数) | 同上,支持基本增减与 CAS 操作 |
AtomicFloat | 线程安全的 float 类型(单精度浮点数) | addAndGet(1.5f)、compareAndSet(2.0f, 3.5f) |
AtomicDouble | 线程安全的 double 类型(双精度浮点数) | 同上,注意浮点数 CAS 依赖 doubleToLongBits 转换,无精度丢失 |
典型用法(AtomicLong 统计并发请求数)
import java.util.concurrent.atomic.AtomicLong;
public class AtomicLongDemo {
// 原子长整型,初始值 0
private static final AtomicLong requestCount = new AtomicLong(0);
public static void incrementRequest() {
// 自增1(原子操作,线程安全)
requestCount.incrementAndGet();
}
public static long getTotalRequest() {
return requestCount.get();
}
public static void main(String[] args) throws InterruptedException {
// 1000个线程并发计数
for (int i = 0; i < 1000; i++) {
new Thread(AtomicLongDemo::incrementRequest).start();
}
Thread.sleep(1000);
System.out.println("总请求数:" + getTotalRequest()); // 输出 1000(无并发问题)
}
}
二、数组类型原子类(线程安全的原子数组)
针对数组场景,提供对数组元素的原子操作(避免用锁保护整个数组),支持 int、long、reference(引用)类型数组。
| 原子类 | 功能说明 | 核心方法示例 |
|---|---|---|
AtomicIntegerArray | 线程安全的 int[] 数组,对单个元素的增减、CAS 操作原子化 | incrementAndGet(index)(指定索引自增)、compareAndSet(index, 旧值, 新值) |
AtomicLongArray | 线程安全的 long[] 数组 | 同上,支持长整型数组元素的原子操作 |
AtomicReferenceArray | 线程安全的对象引用数组(Object[]),原子更新数组中的引用 | set(index, 新对象)、compareAndSet(index, 旧对象, 新对象) |
典型用法(AtomicIntegerArray 并发更新数组元素)
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicArrayDemo {
public static void main(String[] args) throws InterruptedException {
// 初始化原子数组:长度5,初始值0
AtomicIntegerArray array = new AtomicIntegerArray(5);
// 100个线程,每个线程对数组所有元素自增1
for (int i = 0; i < 100; i++) {
new Thread(() -> {
for (int j = 0; j < array.length(); j++) {
array.incrementAndGet(j);
}
}).start();
}
Thread.sleep(1000);
// 输出每个元素的值(均为100,无并发问题)
for (int j = 0; j < array.length(); j++) {
System.out.println("array[" + j + "] = " + array.get(j));
}
}
}
三、引用类型原子类(原子更新对象引用 / 属性)
解决 “对象引用的原子替换” 和 “对象属性的原子更新” 问题,支持多字段原子更新、版本号控制等场景,避免对象引用修改的线程安全问题(如 obj = new Object() 非原子操作)。
| 原子类 | 功能说明 | 核心方法示例 |
|---|---|---|
AtomicReference<V> | 原子更新对象引用(如原子替换整个对象) | compareAndSet(旧对象, 新对象)、getAndSet(新对象) |
AtomicStampedReference<V> | 带版本号的引用原子类(解决 CAS “ABA 问题”) | compareAndSet(旧对象, 新对象, 旧版本, 新版本)、getStamp()(获取版本号) |
AtomicMarkableReference<V> | 带标记位的引用原子类(简化版 AtomicStampedReference,标记位仅 boolean) | compareAndSet(旧对象, 新对象, 旧标记, 新标记)、isMarked()(获取标记) |
关键场景:解决 ABA 问题(AtomicStampedReference)
ABA 问题:线程 1 将值从 A 改为 B 再改回 A,线程 2 的 CAS 操作误以为 A 未被修改,导致错误。AtomicStampedReference 用 “版本号” 避免此问题:
import java.util.concurrent.atomic.AtomicStampedReference;
public class ABAProblemDemo {
public static void main(String[] args) {
String oldValue = "A";
String newValue1 = "B";
String newValue2 = "A"; // 模拟 ABA 中的第二次 A
// 初始化:引用值为 A,版本号为 1
AtomicStampedReference<String> stampedRef = new AtomicStampedReference<>(oldValue, 1);
// 线程1:模拟 ABA 操作(A→B→A,版本号从1→2→3)
new Thread(() -> {
int stamp = stampedRef.getStamp(); // 获取当前版本 1
System.out.println("线程1:当前值=" + stampedRef.getReference() + ",版本=" + stamp);
// 1. A→B(版本1→2)
stampedRef.compareAndSet(oldValue, newValue1, stamp, stamp + 1);
// 2. B→A(版本2→3)
stampedRef.compareAndSet(newValue1, newValue2, 2, 3);
System.out.println("线程1:ABA操作后,值=" + stampedRef.getReference() + ",版本=" + stampedRef.getStamp());
}).start();
// 线程2:尝试用旧版本(1)修改 A→C(预期失败,因为版本已变)
new Thread(() -> {
try {
Thread.sleep(1000); // 等待线程1完成 ABA 操作
int oldStamp = 1; // 线程2一开始获取的版本号(未更新)
boolean success = stampedRef.compareAndSet(oldValue, "C", oldStamp, oldStamp + 1);
System.out.println("线程2:修改是否成功?" + success); // 输出 false
System.out.println("线程2:最终值=" + stampedRef.getReference() + ",版本=" + stampedRef.getStamp()); // 版本3
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
四、字段更新器原子类(原子更新对象的普通字段)
无需修改目标类的代码,即可对其 非 volatile 字段 进行原子更新(底层通过反射实现),适用于无法直接修改目标类(如第三方库类)的场景。
| 原子类 | 功能说明 | 核心方法示例 |
|---|---|---|
AtomicIntegerFieldUpdater<T> | 原子更新对象的 int 类型字段(字段必须是 volatile 修饰,否则报错) | newUpdater(目标类.class, "字段名")、incrementAndGet(对象实例) |
AtomicLongFieldUpdater<T> | 原子更新对象的 long 类型字段 | 同上,支持长整型字段的原子增减、CAS |
AtomicReferenceFieldUpdater<T,V> | 原子更新对象的引用类型字段(V 是字段类型) | compareAndSet(对象实例, 旧值, 新值) |
典型用法(原子更新第三方类的字段)
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
// 第三方类(无法修改源码)
class ThirdPartyClass {
// 必须是 volatile 字段(原子更新器要求)
volatile int count = 0;
public int getCount() { return count; }
}
public class FieldUpdaterDemo {
// 1. 创建字段更新器(指定目标类、字段名)
private static final AtomicIntegerFieldUpdater<ThirdPartyClass> updater =
AtomicIntegerFieldUpdater.newUpdater(ThirdPartyClass.class, "count");
public static void main(String[] args) throws InterruptedException {
ThirdPartyClass obj = new ThirdPartyClass();
// 2. 1000个线程并发更新 count 字段
for (int i = 0; i < 1000; i++) {
new Thread(() -> updater.incrementAndGet(obj)).start();
}
Thread.sleep(1000);
System.out.println("最终 count 值:" + obj.getCount()); // 输出 1000(线程安全)
}
}
⚠️ 注意:目标字段必须满足:① 访问权限为 public/protected/ 默认(同包);② 必须被 volatile 修饰(保证可见性);③ 不能是 static 字段(字段更新器针对实例字段)。
五、累加器原子类(高并发场景下的高效累加)
Java 8 新增,专门优化 高并发累加 场景(如统计 QPS、总耗时),性能优于 AtomicLong(底层采用分段累加,减少 CAS 竞争)。
| 原子类 | 功能说明 | 核心方法示例 |
|---|---|---|
LongAdder | 长整型累加器(推荐高并发计数场景,替代 AtomicLong) | add(1)(累加 1)、increment()(自增 1)、sum()(获取总和) |
LongAccumulator | 自定义运算的长整型累加器(支持任意二元运算,如最大值、最小值、累加) | 构造函数 new LongAccumulator(运算器, 初始值)、accumulate(值)(执行运算) |
DoubleAdder | 双精度浮点型累加器(高并发浮点数累加) | add(1.5)、sum() |
DoubleAccumulator | 自定义运算的双精度累加器 | 同上,支持自定义二元运算(如浮点数乘法累积) |
典型用法(LongAdder 高并发计数)
import java.util.concurrent.atomic.LongAdder;
public class LongAdderDemo {
public static void main(String[] args) throws InterruptedException {
LongAdder adder = new LongAdder();
// 10000个线程并发累加(高并发场景下性能优于 AtomicLong)
for (int i = 0; i < 10000; i++) {
new Thread(adder::increment).start();
}
Thread.sleep(2000);
System.out.println("总计数:" + adder.sum()); // 输出 10000
}
}
自定义运算(LongAccumulator 求最大值)
import java.util.concurrent.atomic.LongAccumulator;
public class LongAccumulatorDemo {
public static void main(String[] args) {
// 构造函数:(x, y) -> Math.max(x, y) 是自定义运算(求最大值),初始值 0
LongAccumulator maxAccumulator = new LongAccumulator(Math::max, 0);
// 并发更新最大值
new Thread(() -> maxAccumulator.accumulate(10)).start();
new Thread(() -> maxAccumulator.accumulate(20)).start();
new Thread(() -> maxAccumulator.accumulate(15)).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最大值:" + maxAccumulator.get()); // 输出 20
}
}
六、其他特殊原子类
| 原子类 | 功能说明 | 适用场景 |
|---|---|---|
AtomicBoolean | 已归类到 “基本类型”,补充:常用于 “开关状态”(如是否停止服务、是否初始化) | 线程安全的状态标记(替代 volatile boolean + 锁) |
AtomicIntegerArray | 已归类到 “数组类型”,补充:支持对数组元素的原子操作,无需锁整个数组 | 并发修改数组中单个元素(如统计每个用户的访问次数) |
核心选型建议
- 普通计数 / 累加:
- 低并发:
AtomicInteger/AtomicLong(API 简单)。 - 高并发:
LongAdder/DoubleAdder(性能更优)。
- 低并发:
- 对象引用原子替换:
- 无 ABA 问题:
AtomicReference。 - 需避免 ABA 问题:
AtomicStampedReference(版本号)/AtomicMarkableReference(标记位)。
- 无 ABA 问题:
- 数组元素原子操作:
AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray(按需选择类型)。 - 修改第三方类字段:
AtomicIntegerFieldUpdater等字段更新器(反射实现,无需改源码)。 - 自定义运算(如最大值、乘法累积):
LongAccumulator/DoubleAccumulator。
总结
Java 原子类覆盖了 基本类型、数组、引用、字段、累加运算 等几乎所有无锁并发场景,核心优势是 “无锁、高效、线程安全”。选择时需根据具体场景(并发强度、数据类型、是否需要避免 ABA 问题)选型,避免过度使用显式锁导致的性能开销。
540

被折叠的 条评论
为什么被折叠?



