前一阵子看ConcurrentHashMap的源码,其中计数部分提及到LongAdder,之后看了一下LongAdder,发现他们实现的思路是一样的,都是将单节点的并发分散到多个节点。
LongAdder的使用场景也就适用于高并发的情形。比如QPS的计算,在多个线程频发更新QPS的时候,效率会很高。
计数选型:
单线程那直接用Long最好了,并发不高的情况用AtomicLong,并发高时选LongAdder。
AtomicLong,并发都作用到了单一的volatile的value上。
private volatile long value;
而LongAdder将并发分散到了多个Cell上,增加实现比较复杂,但是在高并发场景确实比较优秀。
/**
* Padded variant of AtomicLong supporting only raw accesses plus CAS.
*
* JVM intrinsics note: It would be possible to use a release-only
* form of CAS here, if it were provided.
*/
@sun.misc.Contended static final class Cell {
volatile long value;
Cell(long x) { value = x; }
final boolean cas(long cmp, long val) {
return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long valueOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class> ak = Cell.class;
valueOffset = UNSAFE.objectFieldOffset
(ak.getDeclaredField("value"));
} catch (Exception e) {
throw new Error(e);
}
}
}
/** Number of CPUS, to place bound on table size */
static final int NCPU = Runtime.getRuntime().availableProcessors();
/**
* Table of cells. When non-null, size is a power of 2.
*/
transient volatile Cell[] cells;
LongAdder将竞争数据按照线程的粒度进行分离,相比所有竞争线程对一个共享变量使用CAS不断尝试在性能上要效率多了,这也是为什么在高并发环境下LongAdder要优于AtomicLong的原因。