前面介绍了使用 LongAdder 和 DoubleAdder 来实现 Long 和 Double 类型数据在多线程下安全的、高性能的累加。除了这两个工具类,Java8 还引入了 LongAccumulator 和 DoubleAccumulator 类,提供了比 LongAdder 和 DoubleAdder 类更强大、更灵活的功能,允许你自定义累加规则,并可以为累加器设置非零的初始值。
LongAccumulator 类是一个线程安全的类,用于在高并发环境下执行自定义的 long 类型数值累加操作。
LongAccumulator 和 LongAdder 实现原理类似,内部维护了一个或多个 Cell 对象,用于存储累加的值。每个线程在更新时,会尝试将其值累加到某个 Cell 上,从而减少了全局锁的竞争,提高了并发性能。
LongAccumulator 类提供了一个构造方法,该方法接受两个参数,定义如下:
LongAccumulator(LongBinaryOperator accumulatorFunction, long identity)
参数说明:
LongBinaryOperator accumulatorFunction:这是一个函数式接口的实现,定义了累加操作的规则。它接受两个 long 类型的参数,并返回一个 long 类型的结果。定义如下:
long applyAsLong(long left, long right)
long identity:这是累加器的初始值。
LongAccumulator 主要提供如下函数:
accumulate(long x):将给定的值 x 累加到累加器中。具体的累加规则由构造函数中传入的accumulatorFunction 定义。
get():返回累加器的当前值。这是一个最终一致性操作,即它返回的是累加器在某一时间点的近似值,而不是实时值。
reset():将累加器的值重置为初始值(由构造函数中的identity参数指定)。
假设我们创建一个线程池 executorService,然后创建 10 个线程,每个线程对 LongAccumulator 累加一个数字。LongAccumulator 的初始值为 5。 如下:
package com.hxstrive.jdk8.concurrent.atomic; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.LongAccumulator; /** * LongAccumulator 类 * @author HuangXin * @since 1.0.0 2024/7/9 13:14 */ public class LongAccumulatorDemo1 { public static void main(String[] args) { // 创建线程池 final ExecutorService executorService = Executors.newFixedThreadPool(10); final LongAccumulator longAccumulator = new LongAccumulator(Long::sum, 5); for(int i = 0; i < 10; i++) { final int index = (i + 1) * 100; executorService.submit(() -> longAccumulator.accumulate(index)); } executorService.shutdown(); while(!executorService.isTerminated()) {} System.out.println("value=" + longAccumulator.get()); //value=5505 } }
Java8 中除了 LongAccumulator 类以外,还提供了 DoubleAccumulator 类。
DoubleAccumulator 类是 Java 并发编程库中的一个工具类,它用于多线程环境下对 double 类型数值进行安全地累加。工作方式和 LongAccumulator 基本类似,不过是用于 double 类型的值。
DoubleAccumulator类提供了一个构造方法,该方法接受两个参数:
DoubleAccumulator(DoubleBinaryOperator accumulatorFunction, double identity)
参数说明:
DoubleBinaryOperator accumulatorFunction:这是一个函数式接口的实现,定义了累加操作的规则。它接受两个 double 类型的参数,并返回一个 double 类型的结果。定义如下:
double applyAsDouble(double left, double right)
double identity:这是累加器的初始值。
accumulate(double x):将给定的值x累加到累加器中。具体的累加规则由构造函数中传入的accumulatorFunction定义。
get():返回累加器的当前值。这是一个最终一致性操作,即它返回的是累加器在某一时间点的近似值,而不是实时值。
reset():将累加器的值重置为初始值(由构造函数中的 identity 参数指定)。
doubleValue():与 get() 等价。
floatValue():以浮点形式返回当前值。
getThenReset():相当于调用 get() 之后,调用 reset()。
intValue():以 int 返回当前值。
longValue():以 long 形式返回当前值。
假设我们创建一个线程池 executorService,然后创建 10 个线程,每个线程对 DoubleAccumulator 累加一个数字。DoubleAccumulator 的初始值为 5。 如下:
package com.hxstrive.jdk8.concurrent.atomic; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.DoubleAccumulator; import java.util.concurrent.atomic.LongAccumulator; /** * DoubleAccumulator 类 * @author hxstrive.com */ public class DoubleAccumulatorDemo1 { public static void main(String[] args) { // 创建线程池 final ExecutorService executorService = Executors.newFixedThreadPool(10); // 设置基础值为 5 final DoubleAccumulator doubleAccumulator = new DoubleAccumulator(Double::sum, 5); for(int i = 0; i < 10; i++) { final int index = (i + 1) * 100; executorService.submit(() -> doubleAccumulator.accumulate(index)); } executorService.shutdown(); while(!executorService.isTerminated()) {} System.out.println("value=" + doubleAccumulator.get()); //value=5505.0 System.out.println("value=" + doubleAccumulator.longValue()); //value=5505 } }