java除了AtomicInteger,还有哪些常用的原子类?

48 阅读10分钟

在 java.util.concurrent.atomic 包中,除了最常用的 AtomicInteger,还有一系列针对不同场景设计的原子类,涵盖 基本类型、引用类型、字段更新、复合操作 等,核心均基于 CAS + volatile 实现无锁原子性。以下是 常用原子类分类详解,附功能、场景和示例代码:

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

与 AtomicInteger 原理一致,针对 longboolean 等基本类型,提供原子增减、赋值等操作,避免非原子操作(如 long++)的并发问题。

原子类核心功能适用场景关键方法
AtomicLong长整型的原子增减、累加、赋值大数值计数器(如接口调用量、流量统计)incrementAndGet()addAndGet(long delta)set(long)
AtomicBoolean布尔值的原子切换(true/false)状态标记(如 “是否初始化完成”“是否停止”)compareAndSet(boolean expect, boolean update)set(boolean)
AtomicByte/AtomicShort/AtomicChar字节、短整型、字符的原子操作小范围数值原子更新(如状态码、字符标识)incrementAndGet()getAndSet(byte newValue)

示例:AtomicLong 统计接口调用量

java

运行

import java.util.concurrent.atomic.AtomicLong;

// 接口调用量统计(高并发下原子计数)
public class ApiCounter {
    private final AtomicLong callCount = new AtomicLong(0);

    // 原子自增:每次调用接口时执行
    public void incrementCallCount() {
        callCount.incrementAndGet(); // 等价于 callCount++(原子操作)
    }

    // 原子累加:批量统计时使用
    public void addCallCount(long batch) {
        callCount.addAndGet(batch); // 等价于 callCount += batch(原子操作)
    }

    // 获取当前调用量
    public long getCallCount() {
        return callCount.get();
    }
}

注意事项:

  • AtomicBoolean 无 incrementAndGet() 方法,需通过 compareAndSet 实现状态切换(如 while (!atomicBoolean.compareAndSet(false, true)) {})。
  • 基本类型原子类均不支持 null 值,初始值默认是 0(数值类型)、falseAtomicBoolean)。

二、引用类型原子类(原子更新对象引用)

针对 对象引用 的原子操作,解决 “原子替换对象”“避免引用修改冲突” 等问题,支持泛型,灵活适配各类对象。

原子类核心功能适用场景核心解决问题
AtomicReference<V>引用类型的原子赋值、比较替换原子更新对象实例(如单例对象、配置对象)避免多线程同时修改引用导致的对象不一致
AtomicStampedReference<V>带版本号的引用原子类(版本戳 + 引用)避免 CAS 中的 ABA 问题(如链表操作、缓存更新)解决 “值被修改后又改回原值” 导致的误判
AtomicMarkableReference<V>带标记位的引用原子类(布尔标记 + 引用)只需判断引用是否被修改过(无需版本号)简化版 ABA 问题(仅需 “是否修改” 标记)

1. AtomicReference 示例:原子更新配置对象

java

运行

import java.util.concurrent.atomic.AtomicReference;

// 原子更新配置对象(避免多线程修改配置时出现部分更新)
public class AtomicConfig {
    // 配置对象(不可变类,修改时直接替换整个对象)
    static class Config {
        private final String url;
        private final int timeout;

        public Config(String url, int timeout) {
            this.url = url;
            this.timeout = timeout;
        }

        // getter 方法(无 setter,保证不可变)
        public String getUrl() { return url; }
        public int getTimeout() { return timeout; }
    }

    private final AtomicReference<Config> configRef = new AtomicReference<>(
        new Config("http://default.com", 5000) // 初始配置
    );

    // 原子更新配置(替换整个 Config 对象)
    public void updateConfig(String newUrl, int newTimeout) {
        configRef.set(new Config(newUrl, newTimeout)); // 原子赋值
    }

    // 比较并替换配置(仅当当前配置是预期值时才更新)
    public boolean compareAndUpdateConfig(Config expect, Config update) {
        return configRef.compareAndSet(expect, update);
    }

    public Config getConfig() {
        return configRef.get();
    }
}

2. AtomicStampedReference 示例:解决 ABA 问题

java

运行

import java.util.concurrent.atomic.AtomicStampedReference;

// 用 AtomicStampedReference 避免 ABA 问题
public class ABAExample {
    public static void main(String[] args) {
        // 初始值:"A",版本戳:0
        AtomicStampedReference<String> stampedRef = new AtomicStampedReference<>("A", 0);

        // 线程1:尝试将 "A" 改为 "C"(预期版本 0)
        new Thread(() -> {
            int stamp = stampedRef.getStamp(); // 获取当前版本戳:0
            System.out.println("线程1:当前值=" + stampedRef.getReference() + ", 版本=" + stamp);

            // 模拟延迟(让线程2先执行 ABA 操作)
            try { Thread.sleep(1000); } catch (InterruptedException e) {}

            // 比较并替换:预期值 "A",预期版本 0,更新值 "C",新版本 1
            boolean success = stampedRef.compareAndSet("A", "C", stamp, stamp + 1);
            System.out.println("线程1:更新是否成功?" + success); // 输出 false(版本已变)
        }).start();

        // 线程2:执行 ABA 操作(A → B → A)
        new Thread(() -> {
            int stamp = stampedRef.getStamp(); // 版本 0
            // 1. A → B(版本 0 → 1)
            stampedRef.compareAndSet("A", "B", stamp, stamp + 1);
            System.out.println("线程2:A → B,新版本=" + stampedRef.getStamp());

            // 2. B → A(版本 1 → 2)
            stamp = stampedRef.getStamp();
            stampedRef.compareAndSet("B", "A", stamp, stamp + 1);
            System.out.println("线程2:B → A,新版本=" + stampedRef.getStamp());
        }).start();
    }
}
  • 结果:线程 1 延迟后尝试更新时,版本戳已从 0 变为 2,compareAndSet 失败,避免了 ABA 漏洞。

注意事项:

  • AtomicStampedReference 的版本戳是 int 类型(可循环,但需避免频繁溢出),AtomicMarkableReference 的标记位是 boolean(仅需判断 “是否修改”)。
  • 引用类型原子类更新的是 “引用本身”,而非对象内部的字段(若需更新对象字段,用字段更新器原子类)。

三、字段更新器原子类(原子更新对象字段)

无需修改原有类的代码(无需让字段成为 volatile 或原子类),通过 反射机制 原子更新对象的实例字段或静态字段,适用于 “无法修改原有类” 的场景(如第三方库类)。

原子类核心功能适用场景关键限制
AtomicIntegerFieldUpdater原子更新对象的 int 类型字段第三方类的 int 字段原子增减字段必须是 volatile(否则报错)、非 final
AtomicLongFieldUpdater原子更新对象的 long 类型字段第三方类的 long 字段原子累加同上
AtomicReferenceFieldUpdater<V, T>原子更新对象的引用类型字段第三方类的引用字段原子替换字段必须是 volatile、非 final、非 private

示例:AtomicIntegerFieldUpdater 更新第三方类字段

java

运行

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

// 第三方类(无法修改源码,字段是 volatile int)
class ThirdPartyClass {
    volatile int count; // 必须是 volatile(否则更新器无法工作)

    public ThirdPartyClass(int count) {
        this.count = count;
    }

    public int getCount() {
        return count;
    }
}

// 用字段更新器原子操作第三方类的 count 字段
public class FieldUpdaterExample {
    // 1. 创建字段更新器:指定目标类、字段名
    private static final AtomicIntegerFieldUpdater<ThirdPartyClass> COUNT_UPDATER =
        AtomicIntegerFieldUpdater.newUpdater(ThirdPartyClass.class, "count");

    public static void main(String[] args) {
        ThirdPartyClass obj = new ThirdPartyClass(10);

        // 2. 原子自增(count = 10 → 11)
        COUNT_UPDATER.incrementAndGet(obj);
        System.out.println(obj.getCount()); // 输出 11

        // 3. 原子累加(count = 11 → 14)
        COUNT_UPDATER.addAndGet(obj, 3);
        System.out.println(obj.getCount()); // 输出 14

        // 4. 比较并替换(预期 14 → 更新为 20)
        boolean success = COUNT_UPDATER.compareAndSet(obj, 14, 20);
        System.out.println("替换是否成功?" + success); // 输出 true
        System.out.println(obj.getCount()); // 输出 20
    }
}

注意事项:

  • 目标字段必须是 volatile(保证可见性,否则 JVM 会抛出 IllegalArgumentException)。
  • 字段不能是 final(final 字段不可修改,更新器无法操作)。
  • 字段访问权限:若字段是 private,更新器无法通过反射访问(需字段是 publicprotected 或同一包下的默认访问权限)。

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

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

原子类核心功能适用场景关键方法
AtomicIntegerArray原子更新 int[] 数组的元素原子计数器数组、分布式 ID 生成器数组getAndIncrement(int index)compareAndSet(int index, int expect, int update)
AtomicLongArray原子更新 long[] 数组的元素大数据量统计数组(如分桶计数)同上(参数为 long 类型)
AtomicReferenceArray<V>原子更新引用类型数组的元素原子更新对象数组(如缓存数组)set(int index, V newValue)compareAndSet(...)

示例:AtomicIntegerArray 分桶原子计数

java

运行

import java.util.concurrent.atomic.AtomicIntegerArray;

// 分桶原子计数器(将统计对象按哈希分桶,提升并发性能)
public class AtomicArrayCounter {
    private final AtomicIntegerArray bucketCounts;
    private final int bucketSize; // 分桶数量

    public AtomicArrayCounter(int bucketSize) {
        this.bucketSize = bucketSize;
        this.bucketCounts = new AtomicIntegerArray(bucketSize); // 初始值全为 0
    }

    // 根据 key 的哈希值选择分桶,原子自增
    public void increment(String key) {
        int bucketIndex = Math.abs(key.hashCode()) % bucketSize;
        bucketCounts.getAndIncrement(bucketIndex); // 原子自增对应桶的计数
    }

    // 统计所有桶的总计数
    public int getTotalCount() {
        int total = 0;
        for (int i = 0; i < bucketSize; i++) {
            total += bucketCounts.get(i);
        }
        return total;
    }

    public static void main(String[] args) {
        AtomicArrayCounter counter = new AtomicArrayCounter(10); // 10个分桶

        // 多线程并发计数
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> counter.increment("key-" + Math.random())).start();
        }

        // 等待所有线程执行完成
        try { Thread.sleep(1000); } catch (InterruptedException e) {}

        System.out.println("总计数:" + counter.getTotalCount()); // 输出约 1000(原子性保证准确)
    }
}

注意事项:

  • 数组长度固定(创建后无法扩容),需提前规划分桶数量(如根据并发量设置合理桶数,减少冲突)。
  • 数组元素的原子操作仅针对 “单个索引”,多个索引的操作不保证原子性(如同时修改索引 0 和 1,需额外同步)。

五、复合操作原子类(JDK 8+ 新增,支持复杂原子逻辑)

JDK 8 引入的 DoubleAccumulatorLongAccumulator 等,支持 自定义复合原子操作(如累加、最大值计算、自定义函数),性能比 AtomicLong 更优(高并发下分散竞争)。

原子类核心功能适用场景核心优势
LongAccumulator基于函数的长整型原子累加 / 复合操作复杂统计(如最大值、最小值、自定义聚合)支持自定义 LongBinaryOperator,高并发下性能更优
DoubleAccumulator基于函数的双精度浮点型原子复合操作浮点型数据统计(如平均响应时间)同上(支持 DoubleBinaryOperator
LongAdder长整型原子累加(AtomicLong 的高性能替代)高并发计数器(如 QPS 统计)内部分桶存储,减少 CAS 冲突,高并发下性能远超 AtomicLong
DoubleAdder双精度浮点型原子累加浮点型计数器(如流量统计)同上

示例:LongAdder 高并发计数器(替代 AtomicLong

java

运行

import java.util.concurrent.atomic.LongAdder;

// 高并发计数器:LongAdder 性能优于 AtomicLong(分桶减少冲突)
public class HighConcurrencyCounter {
    private final LongAdder adder = new LongAdder();

    // 原子自增(内部分桶,高并发下冲突更少)
    public void increment() {
        adder.increment();
    }

    // 原子累加
    public void add(long delta) {
        adder.add(delta);
    }

    // 获取总数(合并所有分桶的计数)
    public long getCount() {
        return adder.sum();
    }

    public static void main(String[] args) throws InterruptedException {
        HighConcurrencyCounter counter = new HighConcurrencyCounter();

        // 1000 个线程,每个线程自增 1000 次
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    counter.increment();
                }
            }).start();
        }

        Thread.sleep(2000);
        System.out.println("总计数:" + counter.getCount()); // 输出 1000000(准确)
    }
}

示例:LongAccumulator 计算最大值

java

运行

import java.util.concurrent.atomic.LongAccumulator;

// 用 LongAccumulator 原子计算最大值
public class MaxValueCalculator {
    public static void main(String[] args) {
        // 自定义函数:取两个数的最大值(LongBinaryOperator)
        LongAccumulator maxAccumulator = new LongAccumulator(
            (x, y) -> Math.max(x, y), // 累加器函数
            Long.MIN_VALUE // 初始值
        );

        // 多线程并发更新最大值
        long[] values = {10, 20, 5, 30, 15};
        for (long val : values) {
            new Thread(() -> maxAccumulator.accumulate(val)).start();
        }

        // 等待所有线程执行完成
        try { Thread.sleep(1000); } catch (InterruptedException e) {}

        System.out.println("最大值:" + maxAccumulator.get()); // 输出 30
    }
}

注意事项:

  • LongAdder/DoubleAdder 的 sum() 方法是 “最终一致性”(合并分桶时可能有短暂不一致),适合 “最终统计” 场景(如 QPS 统计),不适合 “实时精确读取”(如事务计数)。
  • 复合操作原子类仅支持 “累加 / 聚合” 类逻辑,复杂复合操作(如 “先判断再更新”)仍需结合锁或其他工具。

六、常用原子类选择总结

场景类型推荐原子类替代方案
单变量 int/long 计数AtomicInteger/AtomicLong高并发用 LongAdder/DoubleAdder
布尔状态标记AtomicBooleanAtomicReference<Boolean>
原子更新对象引用AtomicReference需避免 ABA 用 AtomicStampedReference
第三方类字段原子更新AtomicIntegerFieldUpdater 等无法修改字段时的唯一选择
数组元素原子操作AtomicIntegerArray 等手动加锁(性能差)
复杂聚合操作(最大值 / 自定义)LongAccumulator/DoubleAccumulator锁 + 普通变量(代码繁琐)
高并发计数器(QPS / 流量)LongAdderAtomicLong(低并发可用)

核心原则

  1. 简单计数 / 状态切换:优先用 AtomicInteger/AtomicBoolean(API 简洁,无学习成本)。
  2. 高并发计数:用 LongAdder(分桶减少冲突,性能最优)。
  3. 引用原子替换:用 AtomicReference(普通场景)或 AtomicStampedReference(ABA 问题)。
  4. 第三方类字段更新:用字段更新器原子类(无需修改源码)。
  5. 复杂聚合逻辑:用 LongAccumulator/DoubleAccumulator(自定义函数,灵活高效)。