并发计数 / 对象更新不用锁?Java 原子类(含累加器 / 字段更新器)全攻略

Java 的 java.util.concurrent.atomic 包提供了一系列 原子类,基于 CAS(Compare-and-Swap)操作实现无锁线程安全,避免了显式锁的开销。除了常用的 AtomicInteger,原子类可按功能分为 6 大类,覆盖数值操作、引用操作、数组、字段更新等场景,以下是完整分类与核心用法:

一、基本类型原子类(替换基础类型的线程安全版本)

核心是对 intlongbooleandouble 等基本类型提供原子的增减、赋值、比较交换操作,底层直接操作内存地址,性能优于锁。

原子类功能说明核心方法示例
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(无并发问题)
    }
}

二、数组类型原子类(线程安全的原子数组)

针对数组场景,提供对数组元素的原子操作(避免用锁保护整个数组),支持 intlongreference(引用)类型数组。

原子类功能说明核心方法示例
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,标记位仅 booleancompareAndSet(旧对象, 新对象, 旧标记, 新标记)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长整型累加器(推荐高并发计数场景,替代 AtomicLongadd(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已归类到 “数组类型”,补充:支持对数组元素的原子操作,无需锁整个数组并发修改数组中单个元素(如统计每个用户的访问次数)

核心选型建议

  1. 普通计数 / 累加
    • 低并发:AtomicInteger/AtomicLong(API 简单)。
    • 高并发:LongAdder/DoubleAdder(性能更优)。
  2. 对象引用原子替换
    • 无 ABA 问题:AtomicReference
    • 需避免 ABA 问题:AtomicStampedReference(版本号)/AtomicMarkableReference(标记位)。
  3. 数组元素原子操作AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray(按需选择类型)。
  4. 修改第三方类字段AtomicIntegerFieldUpdater 等字段更新器(反射实现,无需改源码)。
  5. 自定义运算(如最大值、乘法累积)LongAccumulator/DoubleAccumulator

总结

Java 原子类覆盖了 基本类型、数组、引用、字段、累加运算 等几乎所有无锁并发场景,核心优势是 “无锁、高效、线程安全”。选择时需根据具体场景(并发强度、数据类型、是否需要避免 ABA 问题)选型,避免过度使用显式锁导致的性能开销。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

canjun_wen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值