当前位置: 首页 > 工具软件 > JUCA > 使用案例 >

学习juca:LongAdder(1.8)

陆晓博
2023-12-01

LongAdder是什么?

很多人根本连API文档都没有好好读,就喜欢吵着“JUC大法好,底层实现妙妙妙”,然后就钻入低层实现的分析了,浮躁是搞技术的大忌。

LongAdder的类文档说明如下:

一个或多个变量一起来维持一个初始为0的long类型的和。当更新(add())跨线程竞争时,变量集合可能会动态增长以减少竞争。方法sum()(或者,等效地,value())返回维持sum的变量之间的当前和。

当多个线程更新用于收集统计信息等目的的公共和时,而不是细粒度同步控制,这个类通常比AtomicLong更好。在更新竞争较低的情况下,这两个类具有相似的特性。但是在高竞争情况下,这一类的预期吞吐量明显更高,代价是更高的空间消耗。

LongAdder可以与ConcurrentHashMap一起使用,以维护可伸缩的frequency map(histogram或multiset的形式)。例如,要将计数添加到ConcurrentHashMap<String,LongAdder> freqs中,初始化(如果尚未存在),可以使用freqs.computeIfAbsent(k -> new LongAdder()).increment();

这个类扩展了Number,但没有定义equals、hashCode和comparTo等方法,因为实例预计会发生突变,因此不适合用作集合键。

从它的接口 API来看,就是一个累加器。

(接口API 略)

因此,我们来回答heading中的问题:什么是LongAdder?

文档对该类的功能和缺陷说得明明白白。无论它底层实现多么复杂,多么精妙,该类本质是构建在经典的并发计数器模型1上的并发计数(累加)器

与AtomicLong比较

AtomicLong表示的是提供原子更新方式的long值。虽然AtomicLong与LongAdder都可以用来维护一个值,而且都可以用来实现“adder”,但它们的本质区别在于,它们是不同的抽象
理解这一点很重要,虽然它们使用上存在不同,如:

  • AtomicLong可以使用封装了“a++”,"++a"类似的操作,而LongAdder没有提供。
  • LongAdder作为“adder”能拥有比AtomicLong更好的性能。

它们的区别不是因为区别而区别,而是因为它们表示的抽象不同,用途不同,而拥有独有的,或专门优化过的功能。

实现

个人认为,逐行代码分析这种并发类的实现意义不大,因为大部分Java程序员没有那个水平能够真正理解具体的实现细节,也不能亲自写出一个不包含阻塞锁的高质量并发工具类。没有相关的专业背景或者训练,普通开发人员看这种源码都是雾里看花。

当然我并不认为喜欢研究底层细节是错误的,只是个人认为收益不大。如果真对并发技术感兴趣,应该找一些合理的,能够构建相关知识框架的资料,从入门开始逐步积累,这才符合正确的学习曲线。当然,另一种可能是为了应付面试,没办法,面试官都爱问,那确实需要好好准备。

要分析LongAdder的原理,首先要看他的父类Striped64,其封装了整个底层的核心部分。
Striped64

因为父类封装了模板算法,看懂了父类就差不多了,LongAdder本身并没有多少比较难以理解的地方。

add()

首先是add()方法,它会首先尝试对base进行CAS加,再尝试对cell进行CAS加,最后再使用Striped64的更新操作。

    public void add(long x) {
        Cell[] as; long b, v; int m; Cell a;
        if ((as = cells) != null || !casBase(b = base, b + x)) {//尝试对base进行CAS操作
            boolean uncontended = true;
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[getProbe() & m]) == null ||
                !(uncontended = a.cas(v = a.value, v + x)))//尝试对cell进行CAS操作
                longAccumulate(x, null, uncontended);//由父类的更新操作进行处理
        }
    }

sum()

直接简单粗暴的累加,每个值都是volatile修饰的。不需要做任何同步处理,关键在于你如何理解并发数据结构的正确性。

    public long sum() {
        Cell[] as = cells; Cell a;
        long sum = base;
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    sum += a.value;
            }
        }
        return sum;
    }

  1. 《OS-TEP》29 并发数据结构--29.1并发计数器

转载于:https://www.cnblogs.com/redreampt/p/9444283.html

 类似资料: