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 生成器都基于它们实现。

相关推荐
一颗宁檬不酸43 分钟前
Vue.js 初学者基础知识点总结 第一弹
前端·javascript·vue.js
技术不打烊43 分钟前
3步吃透 Go 标准库 HTTP/TLS,让你的 Web 服务器直接起飞
后端
xiaoxue..1 小时前
解析 LocalStorage与事件委托在前端数据持久化中的应用
前端·javascript·面试
盛小夏2点0版1 小时前
🚀 Java 小白到高手:一篇吃透语法、集合、并发、Web 全流程!
后端
Mintopia1 小时前
「无界」全局浮窗组件设计与父子组件最佳实践
前端·前端框架·前端工程化
小马爱打代码1 小时前
Spring Boot:DTO、VO、BO、Entity 的正确工程化分层
java·spring boot·后端
u***B7921 小时前
Spring Boot实时推送技术详解:三个经典案例
spring boot·后端·状态模式
@cc小鱼仔仔1 小时前
vue 知识点
前端·javascript·vue.js