LongAdder是Java 8中推出了一个新的类,主要是为了解决AtomicLong在多线程竞争激烈的情况下性能并不高的问题。它主要是采用分段+CAS的方式来提升原子操作的性能。
相比于AtomicLong,LongAdder有更好的性能,但是LongAdder是典型的以空间换时间的实现方式,所以他所需要用到的空间更大, 而且LongAdder可能存在结果不准确的问题,而AtomicLong并不会。
AtomicLong和AtomicInteger、AtomicDouble 等其他原子操作的类一样,都是基于Unsafe 实现的。Unsafe 是一个用来进行硬件级别的原子操作的工具类。在AtomicLong 中定义如下:
private static final Unsafe unsafe = Unsafe.getUnsafe();
并且通过在类中定义一个volatile的变量用来计数:
private volatile long value;
以下几个常用的方法具体实现如下:
public final long incrementAndGet() {
return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}
public final long addAndGet(long delta) {
return unsafe.getAndAddLong(this, valueOffset, delta) + delta;
}
public final boolean compareAndSet(long expect, long update) {
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
public final long getAndSet(long newValue) {
return unsafe.getAndSetLong(this, valueOffset, newValue);
}
可以看到,都是基于unsafe来实现的,其实就是调用了底层的CAS操作来进行原子操作的。
JDK 1.8中的LongAdder继承自抽象类java.util.concurrent.atomic.Striped64 的,这个类也是JDK 1.8中新增的,他主要就是用来在给并发场景中提供计数支持的。
Striped64的设计思路和ConcurrentHashMap类似,都是希望通过分散竞争的方式来提升并发的性能。再具体是线上,主要依赖了其中的以下两个字段:
/**
* Table of cells. When non-null, size is a power of 2.
*/
transient volatile Cell[] cells;
/**
* Base value, used mainly when there is no contention, but also as
* a fallback during table initialization races. Updated via CAS.
*/
transient volatile long base;
通过查看LongAdder中的add方法的代码,我们其实就能很容易的理解他的实现细节:
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
if ((as = cells) != null || !casBase(b = base, b + x)) {
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
longAccumulate(x, null, uncontended);
}
}
首先就是先尝试通过CAS更新计数器base的值,如果在竞争不激烈的情况下,是可以直接更新成功的,如果直接成功那就和AtomicLong一样了。
但是如果更新失败,那么则是认为当前的并发竞争比较激烈,那么就会尝试通过cells数组来分散计数
Striped64根据线程来计算哈希,然后将不同的线程分散到不同的Cell数组的index上,然后这个线程的计数内容就会保存在该Cell的位置上面,基于这种设计,最后的总计数需要结合base以及散落在Cell数组中的计数内容。
是不是看上去和ConcurrentHashMap的分段锁很像。
当需要进行统计数量的时候,则需要结合base和cells一起做统计,其实就是把他们的值都加在一起。
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;
}