在 java.util.concurrent.atomic 包中,除了 AtomicInteger,还有一系列针对不同场景(基本类型、引用类型、数组、字段更新、高并发计数等)的原子类,核心均基于 CAS + volatile 实现无锁原子性。以下是 最常用、最实用的原子类分类详解,附功能、场景和极简示例,方便实际开发选型:
一、基本类型原子类(替代基础类型的原子操作)
与 AtomicInteger 原理一致,针对 long、boolean 等基本类型,解决 "i++、long++" 等非原子操作的并发问题,API 简洁直观。
| 原子类 | 核心功能 | 适用场景 | 关键方法示例 |
|---|---|---|---|
AtomicLong |
长整型原子增减、累加 | 大数值计数(接口调用量、流量统计) | incrementAndGet()(自增)、addAndGet(10)(累加) |
AtomicBoolean |
布尔值原子切换(true/false) | 状态标记(是否初始化、是否停止) | compareAndSet(false, true)(原子置为 true) |
AtomicByte |
字节型原子操作 | 小范围状态码(如 0-255 状态标识) | getAndIncrement()(自增后返回旧值) |
示例:AtomicLong 统计接口调用量
java
运行
scss
AtomicLong callCount = new AtomicLong(0);
// 每次调用接口时原子自增
callCount.incrementAndGet();
// 批量统计时原子累加
callCount.addAndGet(100);
System.out.println("总调用量:" + callCount.get());
二、引用类型原子类(原子更新对象引用)
针对 对象引用 的原子操作,解决 "多线程同时修改对象引用""ABA 问题" 等场景,支持泛型,适配各类对象。
| 原子类 | 核心功能 | 适用场景 | 核心优势 |
|---|---|---|---|
AtomicReference<V> |
引用类型原子赋值、比较替换 | 原子更新配置对象、单例对象 | 避免引用修改冲突(如多线程替换配置) |
AtomicStampedReference<V> |
带版本号的引用原子类(版本戳 + 引用) | 避免 CAS 中的 ABA 问题(链表、缓存) | 解决 "值改回原值" 导致的误判 |
AtomicMarkableReference<V> |
带布尔标记的引用原子类 | 只需判断引用是否被修改过 | 简化版 ABA 解决方案(无需版本号) |
示例 1:AtomicReference 原子更新配置
java
运行
dart
// 配置对象(不可变,修改时直接替换整个对象)
class Config { String url; int timeout; /* 构造器+getter */ }
AtomicReference<Config> configRef = new AtomicReference<>(new Config("http://test.com", 5000));
// 原子替换配置(全量更新,避免部分修改)
configRef.set(new Config("http://prod.com", 3000));
示例 2:AtomicStampedReference 解决 ABA 问题
java
运行
ini
// 初始值"A",版本戳0
AtomicStampedReference<String> stampedRef = new AtomicStampedReference<>("A", 0);
// 比较并替换:预期值"A"、预期版本0 → 更新为"C"、版本1(版本不匹配则失败)
boolean success = stampedRef.compareAndSet("A", "C", 0, 1);
三、数组类型原子类(原子更新数组元素)
针对数组元素的原子操作,避免多线程修改数组时的并发冲突,支持 int、long、引用类型数组。
| 原子类 | 核心功能 | 适用场景 | 关键方法示例 |
|---|---|---|---|
AtomicIntegerArray |
原子更新 int[] 元素 |
分桶计数(如按用户 ID 分桶统计) | getAndIncrement(0)(索引 0 原子自增) |
AtomicLongArray |
原子更新 long[] 元素 |
大数据量分桶统计(如时间片计数) | addAndGet(1, 10)(索引 1 累加 10) |
AtomicReferenceArray<V> |
原子更新引用类型数组元素 | 原子更新缓存数组、对象数组 | compareAndSet(2, oldObj, newObj) |
示例:AtomicIntegerArray 分桶原子计数
java
运行
ini
// 10个分桶的计数器数组(初始值全为0)
AtomicIntegerArray bucketCounts = new AtomicIntegerArray(10);
// 按key哈希选择分桶,原子自增(减少并发冲突)
int bucketIndex = Math.abs("user123".hashCode()) % 10;
bucketCounts.incrementAndGet(bucketIndex);
四、字段更新器原子类(原子更新对象字段)
无需修改原有类源码(如第三方库类),通过 反射 原子更新对象的实例字段或静态字段,要求字段必须是 volatile(保证可见性)。
| 原子类 | 核心功能 | 适用场景 | 关键限制 |
|---|---|---|---|
AtomicIntegerFieldUpdater |
原子更新对象的 int 字段 |
第三方类的 int 字段原子增减 |
字段必须是 volatile、非 final、非 private |
AtomicLongFieldUpdater |
原子更新对象的 long 字段 |
第三方类的 long 字段原子累加 |
同上 |
AtomicReferenceFieldUpdater<V, T> |
原子更新对象的引用字段 | 第三方类的引用字段原子替换 | 同上 |
示例:更新第三方类的 volatile int 字段
java
运行
csharp
// 第三方类(无法修改源码,字段是 volatile int)
class ThirdParty { volatile int count; /* getter */ }
// 创建字段更新器(指定目标类和字段名)
AtomicIntegerFieldUpdater<ThirdParty> updater =
AtomicIntegerFieldUpdater.newUpdater(ThirdParty.class, "count");
ThirdParty obj = new ThirdParty();
updater.incrementAndGet(obj); // 原子自增 count 字段
System.out.println(obj.getCount()); // 输出1
五、高并发复合原子类(JDK8+ 新增,性能优化)
JDK8 引入的高性能原子类,针对高并发场景优化(内部分桶减少 CAS 冲突),适合 QPS 统计、流量计数等高频场景,性能远超 AtomicLong。
| 原子类 | 核心功能 | 适用场景 | 核心优势 |
|---|---|---|---|
LongAdder |
长整型原子累加(高并发优化) | 高并发计数器(QPS、接口调用量) | 分桶存储,减少 CAS 冲突,性能最优 |
DoubleAdder |
双精度浮点型原子累加 | 浮点型计数(流量统计、响应时间总和) | 高并发下比 AtomicDouble 性能好 |
LongAccumulator |
自定义复合原子操作(如最大值) | 复杂统计(最大值、最小值、自定义聚合) | 支持自定义函数,灵活适配复杂逻辑 |
示例:LongAdder 高并发 QPS 统计
java
运行
scss
LongAdder qpsAdder = new LongAdder();
// 每接收一个请求原子自增
qpsAdder.increment();
// 1秒后统计QPS(sum() 合并分桶)
System.out.println("QPS:" + qpsAdder.sum());
示例:LongAccumulator 原子计算最大值
java
运行
scss
// 自定义函数:取两个数的最大值,初始值 Long.MIN_VALUE
LongAccumulator maxAcc = new LongAccumulator(Math::max, Long.MIN_VALUE);
// 多线程并发更新最大值
maxAcc.accumulate(10);
maxAcc.accumulate(20);
maxAcc.accumulate(15);
System.out.println("最大值:" + maxAcc.get()); // 输出20
常用原子类选型总结
| 场景需求 | 推荐原子类 | 备注 |
|---|---|---|
简单 int 计数 |
AtomicInteger |
低并发首选,API 简洁 |
简单 long 计数 / 高并发计数 |
AtomicLong/LongAdder |
高并发用 LongAdder(性能翻倍) |
| 布尔状态标记 | AtomicBoolean |
避免 boolean 非原子切换 |
| 原子更新对象引用 | AtomicReference |
普通场景;需避免 ABA 用 AtomicStampedReference |
| 第三方类字段原子更新 | AtomicIntegerFieldUpdater 等 |
无需修改源码,字段必须是 volatile |
| 分桶计数 / 数组元素原子操作 | AtomicIntegerArray/AtomicLongArray |
减少单变量并发冲突 |
| 复杂聚合(最大值 / 自定义) | LongAccumulator |
支持自定义逻辑,高并发友好 |
核心原则:
- 低并发、简单场景:优先用
AtomicInteger/AtomicLong(学习成本低、API 直观); - 高并发计数:用
LongAdder(分桶无锁,性能最优); - 引用更新 / ABA 问题:用
AtomicReference/AtomicStampedReference; - 第三方类字段 / 数组场景:用字段更新器 / 数组原子类。
java中的tomicInteger/AtomicLong介绍
AtomicInteger 和 AtomicLong 是 java.util.concurrent.atomic 包下最基础、最常用的 原子类 ,核心作用是解决多线程环境下 int/long 类型变量的 原子操作问题 (如 i++、count += n 等非原子操作的并发安全)。它们基于 CAS(Compare-And-Swap)+ volatile 实现无锁原子性,性能优于 synchronized 锁机制(无锁竞争开销)。
一、核心原理
两者原理完全一致,仅支持的数值类型不同(int vs long),核心设计:
-
volatile 可见性 :内部维护的数值变量被
volatile修饰,确保修改后立即写回主内存,其他线程能实时读取最新值(避免缓存一致性问题)。 -
CAS 原子性 :依赖 CPU 提供的原子指令(
cmpxchg),实现 "比较 - 替换" 的原子操作,核心逻辑:- 读取当前内存值
V; - 比较
V与预期值E是否相等; - 若相等,将
V更新为新值N;若不相等,重试(自旋)直到成功。
- 读取当前内存值
-
无锁设计:无需加锁,通过自旋重试解决并发冲突,轻量级且高效(低冲突场景下性能远超锁)。
二、核心功能与 API
两者 API 高度一致(仅参数 / 返回值类型为 int 或 long),以下以 AtomicInteger 为例,AtomicLong 直接替换类型即可:
| 方法分类 | 核心方法 | 功能描述 | 示例(AtomicInteger) |
|---|---|---|---|
| 原子自增 / 自减 | incrementAndGet() |
先自增,返回新值(等价于 ++i) |
atomicInt.incrementAndGet() → 1(初始 0) |
getAndIncrement() |
先返回旧值,再自增(等价于 i++) |
atomicInt.getAndIncrement() → 0(返回后变 1) |
|
decrementAndGet() |
先自减,返回新值(等价于 --i) |
atomicInt.decrementAndGet() → 0(初始 1) |
|
getAndDecrement() |
先返回旧值,再自减(等价于 i--) |
atomicInt.getAndDecrement() → 1(返回后变 0) |
|
| 原子累加 / 赋值 | addAndGet(int delta) |
原子累加 delta,返回新值(等价于 i += delta) |
atomicInt.addAndGet(5) → 5(初始 0) |
getAndAdd(int delta) |
先返回旧值,再累加 delta | atomicInt.getAndAdd(5) → 0(返回后变 5) |
|
set(int newValue) |
原子赋值(直接覆盖旧值,可见性由 volatile 保证) | atomicInt.set(10) → 赋值为 10 |
|
getAndSet(int newValue) |
先返回旧值,再赋值新值 | atomicInt.getAndSet(10) → 5(返回旧值 5,变 10) |
|
| 比较并替换 | compareAndSet(int expect, int update) |
若当前值 == expect,则更新为 update,返回是否成功 | atomicInt.compareAndSet(10, 20) → true(成功) |
| 读取值 | get() |
获取当前值(volatile 保证可见性) | atomicInt.get() → 20 |
三、适用场景
1. 高并发计数器(最核心场景)
-
统计接口调用量、QPS、用户在线数、订单量等。
-
示例:统计接口调用次数
java
运行
javaimport java.util.concurrent.atomic.AtomicInteger; public class ApiCounter { // 原子计数器(初始值0) private final AtomicInteger callCount = new AtomicInteger(0); // 每次调用接口时执行:原子自增 public void recordCall() { callCount.incrementAndGet(); // 线程安全,无并发问题 } // 批量统计:原子累加 public void recordBatchCall(int batchSize) { callCount.addAndGet(batchSize); } // 获取总调用量(volatile 保证可见性) public int getTotalCall() { return callCount.get(); } }
2. 线程安全的序号生成器
-
生成订单 ID、用户 ID、请求序列号等(保证唯一不重复)。
-
示例:生成订单序列号
java
运行
javapublic class OrderIdGenerator { // 原子长整型(支持更大范围,避免溢出) private final AtomicLong orderId = new AtomicLong(10000L); // 原子生成下一个订单ID(自增1) public long generateNextId() { return orderId.incrementAndGet(); // 10001, 10002, ... 无重复 } }
3. 状态标记(简单数值状态)
-
如 "已处理任务数""剩余库存数" 等(需原子更新的数值状态)。
-
示例:原子更新库存
java
运行
javapublic class StockManager { private final AtomicInteger stock = new AtomicInteger(100); // 初始库存100 // 原子扣减库存(返回是否扣减成功) public boolean deductStock(int num) { // 循环重试直到 CAS 成功(避免并发扣减超卖) while (true) { int current = stock.get(); if (current < num) { return false; // 库存不足 } // 比较并替换:当前库存 == current 时,更新为 current - num if (stock.compareAndSet(current, current - num)) { return true; } // 若 CAS 失败(被其他线程修改),自旋重试 } } }
四、AtomicInteger vs AtomicLong 区别
| 特性 | AtomicInteger | AtomicLong |
|---|---|---|
| 支持类型 | int(范围:-2³¹ ~ 2³¹-1) |
long(范围:-2⁶³ ~ 2⁶³-1) |
| 适用场景 | 小范围计数(如接口调用量、小库存) | 大范围计数(如订单 ID、大流量统计) |
| 性能 | 与 AtomicLong 基本一致(JVM 对 long 原子操作优化) |
同上 |
| 溢出风险 | 较高(int 范围有限) |
极低(long 范围足够大) |
注意:
AtomicInteger若用于超大数据计数(如亿级流量),需注意int溢出(可改用AtomicLong或定时重置计数器)。- 64 位 JVM 中,
AtomicLong的 CAS 操作是原子的;32 位 JVM 中,long是 64 位,CAS 需分两次 32 位操作,但 JDK 已通过Unsafe类保证其原子性,无需手动处理。
五、关键注意事项
-
不支持复合逻辑原子性:单个 API 是原子的,但多个原子操作组合(如 "先自增再判断是否超过阈值")不原子,需手动保证:
java
运行
kotlin// 错误:两步操作非原子,可能并发问题 if (atomicInt.incrementAndGet() > 100) { ... } // 正确:用循环 CAS 包裹复合逻辑 while (true) { int current = atomicInt.get(); if (current >= 100) { return; } if (atomicInt.compareAndSet(current, current + 1)) { break; } } -
ABA 问题 :两者均存在 ABA 漏洞(如线程 1 读取值为 A,线程 2 改为 B 再改回 A,线程 1 CAS 仍会成功)。若业务场景需避免 ABA(如链表操作、缓存更新),需改用
AtomicStampedReference(带版本号)。 -
高冲突场景性能下降:若上千线程同时 CAS 操作同一变量,自旋重试会消耗大量 CPU,此时需改用:
- 高并发计数:
LongAdder(JDK8+,分桶减少冲突,性能远超AtomicLong); - 复合操作:
ReentrantLock锁(互斥执行,避免自旋开销)。
- 高并发计数:
-
与
synchronized的选择:- 单变量原子操作(如计数、赋值):优先用
AtomicInteger/AtomicLong(无锁,性能优); - 多变量复合操作(如 "读 - 改 - 写" 多个变量):用
synchronized或Lock(保证整体原子性)。
- 单变量原子操作(如计数、赋值):优先用
六、总结
AtomicInteger 和 AtomicLong 是 Java 并发编程中 最基础、最高效的单变量原子操作工具:
-
核心价值:无锁实现
int/long的原子增减、赋值、比较替换,解决并发计数 / 序号生成等场景的线程安全问题。 -
选型建议:
- 小范围计数 / 状态:用
AtomicInteger(简洁高效); - 大范围计数 / ID 生成:用
AtomicLong(避免溢出); - 高并发亿级计数:用
LongAdder(JDK8+,分桶优化,性能更优); - 复合逻辑 / 多变量操作:用
synchronized或ReentrantLock。
- 小范围计数 / 状态:用
它们是并发编程的 "基础组件",很多框架(如 Spring、Dubbo)的计数器、ID 生成器都基于它们实现。