【小家java】AtomicLong可以抛弃了,请使用LongAdder代替(或使用LongAccumulator)(上)

简介: 【小家java】AtomicLong可以抛弃了,请使用LongAdder代替(或使用LongAccumulator)(上)

前言


如题,如果你对AtomicLong的使用、运行机制还不了解的话,请移步我上一篇博文:【小家java】原子操作你还在用Synchronized?Atomic、LongAdder你真有必要了解一下了


如果你现在是用的JDK还是停留在JDK7及以下,对JDK8没有太多的了解,那么本文的讲述获取能让你又多一个赶紧升级的理由。


LongAdder这个类也许很多人闻所未闻,虽然已经使用JDK8很久了。那本文就是要扫盲啦


LongAdder


DoubleAccumulator、LongAccumulator、DoubleAdder、LongAdder是JDK1.8新增的部分,是对AtomicLong等类的改进。


LongAccumulator与LongAdder在高并发环境下比AtomicLong更高效。


看看LongAdder类的java doc怎么说?

 * <p>This class is usually preferable to {@link AtomicLong} when
 * multiple threads update a common sum that is used for purposes such
 * as collecting statistics, not for fine-grained synchronization
 * control.  Under low update contention, the two classes have similar
 * characteristics. But under high contention, expected throughput of
 * this class is significantly higher, at the expense of higher space
 * consumption.
 *
 * <p>LongAdders can be used with a {@link
 * java.util.concurrent.ConcurrentHashMap} to maintain a scalable
 * frequency map (a form of histogram or multiset). For example, to
 * add a count to a {@code ConcurrentHashMap<String,LongAdder> freqs},
 * initializing if not already present, you can use {@code
 * freqs.computeIfAbsent(k -> new LongAdder()).increment();}


大概我们能抽取一些关键信息总结如下:


LongAdder中会维护一组(一个或多个)变量,这些变量加起来就是要以原子方式更新的long型变量。当更新方法add(long)在线程间竞争时,该组变量可以动态增长以减缓竞争。方法sum()返回当前在维持总和的变量上的总和。 (这种机制特别像分段锁机制)


与AtomicLong相比,LongAdder更多地用于收集统计数据,而不是细粒度的同步控制。在低并发环境下,两者性能很相似。但在高并发环境下,LongAdder有着明显更高的吞吐量,但是有着更高的空间复杂度(缺点就是内存占用偏高点)。


LongAdder是JDK1.8开始出现的,所提供的API基本上可以替换掉原先的AtomicLong。该类的outline如下:

image.png

LongAdder的优化思想


LongAdder所使用的思想就是热点分离,这一点可以类比一下ConcurrentHashMap的设计思想。就是将value值分离成一个数组,当多线程访问时,通过hash算法映射到其中的一个数字进行计数。而最终的结果,就是这些数组的求和累加。这样一来,就减小了锁的粒度。如下图所示:

image.png


在实现的代码中,LongAdder一开始并不会直接使用Cell[]存储。而是先使用一个long类型的base存储,当casBase()出现失败时,则会创建Cell[]。此时,如果在单个Cell上面出现了cell更新冲突,那么会尝试创建新的Cell,或者将Cell[]扩容为2倍。代码如下:

    public void increment() {
        add(1L);
    }
    public void add(long x) {
        Cell[] as; long b, v; int m; Cell a;
        if ((as = cells) != null || !casBase(b = base, b + x)) {// 如cells不为空,直接对cells操作;否则casBase
            boolean uncontended = true;
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[getProbe() & m]) == null ||
                !(uncontended = a.cas(v = a.value, v + x)))    // CAS cell
                longAccumulate(x, null, uncontended);    // 创建新的Cell或者扩容
        }
    }


与其他原子类一样,LongAdder也是基于CAS实现的。


相关文章
|
Java p3c
【Java用法】请使用System.currentTimeMillis()代替new Date().getTime()
【Java用法】请使用System.currentTimeMillis()代替new Date().getTime()
122 0
|
6月前
|
安全 Java 测试技术
解密Java并发中的秘密武器:LongAdder与Atomic类型
解密Java并发中的秘密武器:LongAdder与Atomic类型
325 1
|
5月前
|
算法 Java 容器
深入解析Java并发库(JUC)中的LongAdder
深入解析Java并发库(JUC)中的LongAdder
|
存储 安全 Java
4.2 Java数组性能优化策略:使用ArrayList代替原生数组
4.2 Java数组性能优化策略:使用ArrayList代替原生数组
314 0
|
缓存 算法 安全
Java Review - 并发编程_原子操作类LongAdder & LongAccumulator剖析
Java Review - 并发编程_原子操作类LongAdder & LongAccumulator剖析
121 0
|
缓存 Java API
Java8原子弹类之LongAdder源码分析
JDK 8开始,针对Long型的原子操作, Java又提供了LongAdder. LongAccumulator; 针对Double类型,Java提供了DoubleAdder、DoubleAccumulator。
99 0
Effective Java--第1条静态工厂方法代替构造方法
Effective Java--第1条静态工厂方法代替构造方法
145 0
Effective Java--第1条静态工厂方法代替构造方法
|
安全 IDE JavaScript
为什么要用Kotlin代替Java
为什么要用Kotlin代替Java
|
Java 测试技术 API
【小家java】AtomicLong可以抛弃了,请使用LongAdder代替(或使用LongAccumulator)(下)
【小家java】AtomicLong可以抛弃了,请使用LongAdder代替(或使用LongAccumulator)(下)
|
10天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
下一篇
无影云桌面