一 AtomicLong原理
AtomicLong通过cas+自旋更新AtomicLong中的value值,进而保证value的原子性,N个线程同时改变value的值,只能有一个线程更新成功,其他线程这次的cas是失败的
由于一次只能有一个线程修改成功,其他线程得要自旋,一直到修改失败,在并发量比较高的情况下,可能会导致这些线程占用长时间修改值失败,导致cas次数过多。
二 LongAdder原理
在高并发场景下LongAdder可以显著的提高性能。使用了空间换时间的思想,咋atomiclong中cas对一个value值的修改,使用了一个数组经行分散修改,这样cas修改失败的概率就会小许多。
例如有一个base值,有三个线程要对执行+1操作,这个时候只有一个线程能成功使用cas对base修改成功,其他线程都会转而去修改cell数组的值,例如cell[2],0,1位置都是0,这个时候要对
三 LongAdder源码
- add方法 流程图
public void add(long x) {
// as表示cells的引用
// b 表示获取的base的值
// v 表示期望值
// m为数组的长度
// 表示当前线程路由命中的cell单元格
Cell[] as; long b, v; int m; Cell a;
//条件1:true 表示数组不为空 当前线程应该将数据写入到对应的cell中
false 表示数组未初始化为空,当前线程应该将数据写base中
//条件2 :true 表示cas改变base失败 可能需要重试 或者 扩容
if ((as = cells) != null || !casBase(b = base, b + x)) {
//进入代码块的条件:
1 数组已经出初始化了,当前线程应该将数据写入到对应的cell中
2 数组为空,但是cas改变base出现了竞争
boolean uncontended = true;
//条件1 2:判断数组是否为空,如果不是空,继续判断条件3
//条件3 :getProbe()获取当前线程的hash值(代名词以下都说是hash值),m表示cells长度-1,cells长度 一定是2的次方数,
true :表示当前线程经过路由后对应的数组的桶位为空,需要创建对象
false:说明当前线程对应的cell 不为空,说明 下一步想要将x值 添加到cell中。
//条件4:true 表示cas失败,表明有竞争
false:表示cas成功
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
//都有哪些情况会调用?
//1 cells数组位空
// 2 线程路由后对应下标的cells位空
// 3 cas 修改cells对应的位置失败
longAccumulate(x, null, uncontended);
}
}
- striped64类中属性
当前机器cpu的数量,用来限定数组的大小
static final int NCPU = Runtime.getRuntime().availableProcessors();
用来减少cas替换次数的数组
transient volatile Cell[] cells;
没有发生过竞争时,数据会累加到 base上 | 当cells扩容时,需要将数据写到base中
transient volatile