java中的tomicInteger/AtomicLong介绍

java.util.concurrent.atomic 包中,除了 AtomicInteger,还有一系列针对不同场景(基本类型、引用类型、数组、字段更新、高并发计数等)的原子类,核心均基于 CAS + volatile 实现无锁原子性。以下是 最常用、最实用的原子类分类详解,附功能、场景和极简示例,方便实际开发选型:

一、基本类型原子类(替代基础类型的原子操作)

AtomicInteger 原理一致,针对 longboolean 等基本类型,解决 "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);

三、数组类型原子类(原子更新数组元素)

针对数组元素的原子操作,避免多线程修改数组时的并发冲突,支持 intlong、引用类型数组。

原子类 核心功能 适用场景 关键方法示例
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介绍

AtomicIntegerAtomicLongjava.util.concurrent.atomic 包下最基础、最常用的 原子类 ,核心作用是解决多线程环境下 int/long 类型变量的 原子操作问题 (如 i++count += n 等非原子操作的并发安全)。它们基于 CAS(Compare-And-Swap)+ volatile 实现无锁原子性,性能优于 synchronized 锁机制(无锁竞争开销)。

一、核心原理

两者原理完全一致,仅支持的数值类型不同(int vs long),核心设计:

  1. volatile 可见性 :内部维护的数值变量被 volatile 修饰,确保修改后立即写回主内存,其他线程能实时读取最新值(避免缓存一致性问题)。

  2. CAS 原子性 :依赖 CPU 提供的原子指令(cmpxchg),实现 "比较 - 替换" 的原子操作,核心逻辑:

    • 读取当前内存值 V
    • 比较 V 与预期值 E 是否相等;
    • 若相等,将 V 更新为新值 N;若不相等,重试(自旋)直到成功。
  3. 无锁设计:无需加锁,通过自旋重试解决并发冲突,轻量级且高效(低冲突场景下性能远超锁)。

二、核心功能与 API

两者 API 高度一致(仅参数 / 返回值类型为 intlong),以下以 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

    运行

    java 复制代码
    import 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

    运行

    java 复制代码
    public class OrderIdGenerator {
        // 原子长整型(支持更大范围,避免溢出)
        private final AtomicLong orderId = new AtomicLong(10000L);
    
        // 原子生成下一个订单ID(自增1)
        public long generateNextId() {
            return orderId.incrementAndGet(); // 10001, 10002, ... 无重复
        }
    }

3. 状态标记(简单数值状态)

  • 如 "已处理任务数""剩余库存数" 等(需原子更新的数值状态)。

  • 示例:原子更新库存

    java

    运行

    java 复制代码
    public 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 类保证其原子性,无需手动处理。

五、关键注意事项

  1. 不支持复合逻辑原子性:单个 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;
        }
    }
  2. ABA 问题 :两者均存在 ABA 漏洞(如线程 1 读取值为 A,线程 2 改为 B 再改回 A,线程 1 CAS 仍会成功)。若业务场景需避免 ABA(如链表操作、缓存更新),需改用 AtomicStampedReference(带版本号)。

  3. 高冲突场景性能下降:若上千线程同时 CAS 操作同一变量,自旋重试会消耗大量 CPU,此时需改用:

    • 高并发计数:LongAdder(JDK8+,分桶减少冲突,性能远超 AtomicLong);
    • 复合操作:ReentrantLock 锁(互斥执行,避免自旋开销)。
  4. synchronized 的选择

    • 单变量原子操作(如计数、赋值):优先用 AtomicInteger/AtomicLong(无锁,性能优);
    • 多变量复合操作(如 "读 - 改 - 写" 多个变量):用 synchronizedLock(保证整体原子性)。

六、总结

AtomicIntegerAtomicLong 是 Java 并发编程中 最基础、最高效的单变量原子操作工具

  • 核心价值:无锁实现 int/long 的原子增减、赋值、比较替换,解决并发计数 / 序号生成等场景的线程安全问题。

  • 选型建议:

    • 小范围计数 / 状态:用 AtomicInteger(简洁高效);
    • 大范围计数 / ID 生成:用 AtomicLong(避免溢出);
    • 高并发亿级计数:用 LongAdder(JDK8+,分桶优化,性能更优);
    • 复合逻辑 / 多变量操作:用 synchronizedReentrantLock

它们是并发编程的 "基础组件",很多框架(如 Spring、Dubbo)的计数器、ID 生成器都基于它们实现。

相关推荐
涡能增压发动积18 小时前
同样的代码循环 10次正常 循环 100次就抛异常?自定义 Comparator 的 bug 让我丢尽颜面
后端
Wenweno0o18 小时前
0基础Go语言Eino框架智能体实战-chatModel
开发语言·后端·golang
于慨18 小时前
Lambda 表达式、方法引用(Method Reference)语法
java·前端·servlet
石小石Orz18 小时前
油猴脚本实现生产环境加载本地qiankun子应用
前端·架构
swg32132118 小时前
Spring Boot 3.X Oauth2 认证服务与资源服务
java·spring boot·后端
从前慢丶18 小时前
前端交互规范(Web 端)
前端
tyung18 小时前
一个 main.go 搞定协作白板:你画一笔,全世界都看见
后端·go
gelald18 小时前
SpringBoot - 自动配置原理
java·spring boot·后端
CHU72903518 小时前
便捷约玩,沉浸推理:线上剧本杀APP功能版块设计详解
前端·小程序
GISer_Jing18 小时前
Page-agent MCP结构
前端·人工智能