介绍
LongAddr
是JDK1.8
才有的。其在高并发情况下,相比与AtomicLong
的性能更高。本篇主要分析一下其实现原理。并且与AtomicLong
做一个性能对比测试。
AtomicLong
利用CPU
对CAS
实现的原子化指令实现。
public final long getAndAddLong(Object var1, long var2, long var4) {
long var6;
do {
var6 = this.getLongVolatile(var1, var2);// 先从内存中拿到最新值
// 做比较并交换,(内存地址,偏移量,最新值,准备更新的值)
} while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
return var6;
}
以上代码是CAS
算法的源码。有一个do while
的自旋操作。如果并发量过大。会导致自旋的次数过多。那么性能就下来了。大家都在集中抢占成员变量value
。同一时间有上百个线程在抢value
的更新权限。那不得。。。
既然大家争抢同一资源过于凶猛,狼多肉少,那就多来点肉呗。
把value
值分一下,分成比如4份。比如value=4,分成4份之后,每份都是1,[1,1,1,1],这个时候想要累加1,只需要找到4个值中的一个,累加1即可,如果正好有4个线程的话,各加个的,互不打扰。value = 1+1+1+2 = 5。跟我们想要的结果是一致的。这样,原来的竞争压力就被分散,相当于降低到了原来的1/4。相信这个原理不难理解。
来分析一下源代码:
// LongAddr.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);
}
}
上面这个类真的挺晦涩的。我把它愿意调整下。看的舒服些。愿意没有变化。
public void add(long x) {
Cell[] as; // Cell[]对象是用来存储上文说的value拆分之后的数据的对象
long b, v;
int m;
Cell a;
// 如果cells没有初始化并且通过CAS计算base值成功了,就不用拆分了。直接离开
if ((as = cells) == null && casBase(b = base, b + x)) {
return;
}
boolean uncontended = true;
boolean enterAccumulate =
(as == null) // cells是否初始化
|| (m = as.length -