LongAdder longAdder = new LongAdder();
longAdder.increment(); //依次开始分析
LongAdder的基本思路是分散热点,将value值分散到一个Cell数组中。
第一部分:LongAdder.add()
第二部分:Striped64.longAccumulate
if ((cs = cells) != null || !casBase(b = base, b + x)) //如果没有创建cells,且base压力过大则进入下一个判断if (cs == null || (m = cs.length - 1) < 0 ||(c = cs[getProbe() & m]) == null ||!(uncontended = c.cas(v = c.value, v + x)))
longAccmulate中for循环分三种情况:
case1:cells已经被初始化-------多次重设hash值,多次竞争。谨慎扩容。
case1中的6个分支,我感觉起到的大致作用就是谨慎扩容。
刚开始看源码的时候很奇怪,为什么不是应该确定cells初始化了,且有竞争失败了的情况,要么是cells数组中有的还是null要赋值,要不直接扩容,怎么这么多else if啊?
但仔细看了之后明白自己的想法多么粗糙,如果按我的思路那么基本上竞争失败一次就要扩容,这对空间的消耗太大了,也没考虑CPU是否支持的问题。大佬的代码这些都考虑到了
(1)collide---是否允许扩容
else if (n >= NCPU || cells != cs)
collide = false; // 如果n大于CPU最大线程数量,不可扩容,通过h = advanceProbe(h);修改线程的probe重新尝试
else if (!collide)
collide = true;
else if (cellsBusy == 0 && casCellsBusy())
第一次读的时候感觉collide这个变量没什么作用,但是仔细看了后发现这个变量的作用:(1)如果else if (n >= NCPU || cells != cs)后直接扩容,代码少一次重新设置hash重新竞争的机会,可能造成不必要的扩容
(2)else if (!wasUncontended) //重新竞争一次,通过h = advanceProbe(h);修改线程的probe重新尝试。还是失败也不会因为wasUncontended再去竞争了
(3)else if (c.cas(v = c.value,(fn == null) ? v + x : fn.applyAsLong(v, x))) //每次for循环的case1中 大多要经历这个esle if,如果竞争成功则直接退出for,如果失败代码继续向下走。
case2:cells没有加锁且没有初始化,尝试对它加锁并初始化。
(1)在else if (cellsBusy == 0 && cells == cs && casCellsBusy())之后为什么再进行一次if (cells == cs)判断?
网上大部分解释是:防止多线程同时操作进入else if语句中,防止再new一个cell数组。
但是:casCellsBusys()调用的是一个CAS函数CELLSBUSY.compareAndSet(this, 0, 1);这个函数本身不起到防止多线程同时操作作用吗?那下面的if (cells == cs)判断到底是什么作用?有大佬给我解释一下吗?
case3:cells正在初始化,则将任务分配到base上
第三部分:LongAdder.sum()
注意:sum执行的时候,base和cells是可以更新的,所以LongAdder对比AtomicLong的精度要低。
代码感受:(1)涉及多线程的代码if判断大多使用,双重检测和CAS机制,提高代码的健壮性
(2)条件判断和要执行的任务大多数尽量合并,让代码更加整洁。
标签:LongAdder,cells,collide,else,竞争,AtomicLong,cs,null From: https://www.cnblogs.com/nowpan/p/17663183.html