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;
}
《OS-TEP》29 并发数据结构--29.1并发计数器↩