Java并发工具:LongAdder

createh52周前 (05-13)技术教程4

LongAdder 是 Java 中
java.util.concurrent.atomic 包下的一个类,从 Java 8 开始引入。它是一个可伸缩的并发累加器,适用于高并发场景下对长整型(long)数值进行高效的递增、递减或加法操作。其核心设计通过分散热点竞争提升性能。

基本概念

LongAdder 的设计目的是为了在高并发写入的情况下提供比 AtomicLong 更好的性能。

- AtomicLong 在并发写入时容易因为线程竞争而导致大量 CAS(Compare and Swap)失败,从而降低效率。

- LongAdder 使用了分段锁的思想,内部维护多个计数单元(Cell),每个线程根据其线程局部变量访问不同的单元,减少竞争。

- 最终结果通过 sum() 方法汇总所有单元的值。

核心结构

继承关系:LongAdder继承自Striped64类,内部维护一个Cell数组和base基础值。

真实值计算:实际值 = base + 所有Cell元素的value值之和。

避免伪共享:Cell类使用@sun.misc.Contended注解填充缓存行,减少伪共享问题。

核心机制

(1) 初始化与基础更新

初始无竞争:当线程未发生竞争时,直接通过CAS操作更新base值。

竞争触发初始化:若CAS更新base失败,则初始化Cell数组(默认大小为2)。

(2) 线程访问策略

哈希映射:通过
ThreadLocalRandom.getProbe()生成线程的哈希值,与Cell数组长度取模确定访问的Cell元素。

冲突处理:若目标Cell槽位已被占用,尝试其他槽位或触发扩容。

(3) 扩容机制

扩容条件:当Cell数组的某个槽位竞争激烈(CAS失败次数多),且数组未达到CPU核心数时,触发扩容。

扩容方式:数组大小翻倍(类似ConcurrentHashMap),直到达到硬件支持的最大并行度。

(4) 原子性保证

Cell操作:每个Cell内部通过CAS更新value值。

全局锁控制:初始化、扩容等操作使用cellsBusy自旋锁(CAS控制)保证线程安全。

适用场景

  • 高频写、低频读:如统计计数场景(如QPS统计),此时读取操作需遍历所有Cell求和,性能略低。
  • 替代AtomicLong:在高并发写场景下,LongAdder性能显著优于AtomicLong,但会占用更多内存。
  • 不适用场景:需要强一致性的实时读取场景(如余额计算)。

示例代码

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.LongAdder;

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

        ExecutorService executor = Executors.newFixedThreadPool(10);

        for (int i = 0; i < 1000; i++) {
            executor.submit(counter::increment);
        }

        executor.shutdown();
        while (!executor.isTerminated()) {
            // 等待所有任务完成
        }

        System.out.println("Counter value: " + counter.sum()); // 输出:1000
    }
}

LongAdder vs AtomicLong

特性

LongAdder

AtomicLong

写多读少

高效

效率低(CAS 失败多)

读频繁

sum() 可能不一致

实时一致性

是否最终一致

内存占用

稍大(维护 Cell 数组)

较小

适用场景

高并发计数器(如统计请求量)

单线程或低并发,需要强一致性

注意事项

- sum() 返回的是调用时刻的一个近似值,在并发修改时可能不是实时准确的,但在没有写入完成后是稳定的。

- 如果你要求每次读取都必须是最新的值(强一致性),那么应使用 AtomicLong。

- LongAdder 不支持构造函数设置初始值,初始值为 0。

- 有对应的 DoubleAdder 类用于浮点类型。

相关文章

1.3、Java运算符全解析

在Java编程语言中,**运算符(Operators)**是用于执行特定操作的符号。它们可以操作一个或多个操作数,并根据其功能返回结果。本文将详细介绍Java中的各种运算符及其使用方法。一、算术运算符...

减法变加法的过程

5-1=4 变成加法分三步操作,取模、相加、去掉多余的位 1.取模:我们在这讨论的是十进制运算,那么模就是10,那么对-1取模就得到了9 2.相加: 5+9=14 3.去掉多余的位数:14去掉1=4...

Java BigDecimal类型的 加减乘除运算和比较

加法:add 减法:subtract 乘法:multiply 除法:divideBigDecimal bd1 = new BigDecimal("15"); BigDecimal bd...

Java并发工具:LongAccumulator

LongAccumulator 是 Java 中 java.util.concurrent.atomic 包下的一个类,和 LongAdder 一样从 Java 8 引入。它是一个支持自定义累加函数的...

Atomic升级Adder在升级Accumulator类

Java架构师专题并发编程专题-CAS原理(节选):Atomic升级Adder在升级Accumulator类。它还有一个叫Accumulator的类。Accumulator是针对ada类的增强版,因为...