④. LongAdder.java
LongAdder.java (1).baseOK,直接通过casBase进行处理 (2).base不够用了,开始新建一个cell数组,初始值为2 (3).当多个线程竞争同一个Cell比较激烈时,可能就要对Cell[ ]扩容 public void add(long x) { //as是striped64中的cells数组属性 //b是striped64中的base属性 //v是当前线程hash到的cell中存储的值 //m是cells的长度减1,hash时作为掩码使用 //a时当前线程hash到的cell Cell[] as; long b, v; int m; Cell a; /** 首次首线程(as = cells) != null)一定是false,此时走casBase方法,以CAS的方式更新base值, 且只有当cas失败时,才会走到if中 条件1:cells不为空,说明出现过竞争,cell[]已创建 条件2:cas操作base失败,说明其他线程先一步修改了base正在出现竞争 */ if ((as = cells) != null || !casBase(b = base, b + x)) { //true无竞争 fasle表示竞争激烈,多个线程hash到同一个cell,可能要扩容 boolean uncontended = true; /* 条件1:cells为空,说明正在出现竞争,上面是从条件2过来的,说明!casBase(b = base, b + x))=true 会通过调用longAccumulate(x, null, uncontended)新建一个数组,默认长度是2 条件2:默认会新建一个数组长度为2的数组,m = as.length - 1) < 0 应该不会出现, 条件3:当前线程所在的cell为空,说明当前线程还没有更新过cell,应初始化一个cell。 a = as[getProbe() & m]) == null,如果cell为空,进行一个初始化的处理 条件4:更新当前线程所在的cell失败,说明现在竞争很激烈,多个线程hash到同一个Cell,应扩容 (如果是cell中有一个线程操作,这个时候,通过a.cas(v = a.value, v + x)可以进行处理,返回的结果是true) **/ if (as == null || (m = as.length - 1) < 0 || //getProbe( )方法返回的时线程中的threadLocalRandomProbe字段 //它是通过随机数生成的一个值,对于一个确定的线程这个值是固定的(除非刻意修改它) (a = as[getProbe() & m]) == null || !(uncontended = a.cas(v = a.value, v + x))) //调用Striped64中的方法处理 longAccumulate(x, null, uncontended); } } Striped64.java abstract class Striped64 extends Number { static final int NCPU = Runtime.getRuntime().availableProcessors(); transient volatile Cell[] cells; transient volatile long base; transient volatile int cellsBusy; //低并发状态,还没有新建cell数组且写入进入base,刚好够用 //base罩得住,不用上cell数组 final boolean casBase(long cmp, long val) { //当前对象,在base位置上,将base(类似于AtomicLong中全局的value值),将base=0(cmp)改为1(value) return UNSAFE.compareAndSwapLong(this, BASE, cmp, val); } }
⑤. sum( )
- ①. sum( )会将所有Cell数组中的value和base累加作为返回值
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; }
②. 核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中去,从而降级更新热点
③. 为啥高并发下sum的值不精确?
sum执行时,并没有限制对base和cells的更新(一句要命的话)。所以LongAdder不是强一致性,它是最终一致性的
首先,最终返回的sum局部变量,初始被赋值为base,而最终返回时,很可能base已经被更新了,而此时局部变量sum不会更新,造成不一致
其次,这里对cell的读取也无法保证是最后一次写入的值。所以,sum方法在没有并发的情况下,可以获得正确的结果
⑥. 关于AtomicLong和LongAdder区别