咱们知道,AtomicLong的实现方式是内部有个value 变量,当多线程并发自增,自减时,均经过CAS 指令从机器指令级别操做保证并发的原子性。html
// setup to use Unsafe.compareAndSwapLong for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; /** * Records whether the underlying JVM supports lockless * compareAndSwap for longs. While the Unsafe.compareAndSwapLong * method works in either case, some constructions should be * handled at Java level to avoid locking user-visible locks. */ static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8(); /** * Returns whether underlying JVM supports lockless CompareAndSet * for longs. Called only once and cached in VM_SUPPORTS_LONG_CAS. */ private static native boolean VMSupportsCS8(); static { try { valueOffset = unsafe.objectFieldOffset (AtomicLong.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile long value; /** * Creates a new AtomicLong with the given initial value. * * @param initialValue the initial value */ public AtomicLong(long initialValue) { value = initialValue; } /** * Creates a new AtomicLong with initial value {@code 0}. */ public AtomicLong() { }
先看LongAdder的add()方法:shell
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); } }
Cell是Striped64的一个内部类,顾名思义,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); } } }
Cell内部有一个很是重要的value变量,而且提供了一个更新其值的cas()方法。多线程
回到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); } }
这里,我有个疑问,AtomicLong已经使用CAS指令,很是高效了(比起各类锁),LongAdder若是仍是用CAS指令更新值,怎么可能比AtomicLong高效了? 况且内部还这么多判断!!!less
这是我开始时最大的疑问,因此,我猜测,难道有比CAS指令更高效的方式出现了? 带着这个疑问,继续。dom
第一if 判断,第一次调用的时候cells数组确定为null,所以,进入casBase方法:ide
/** * CASes the base field. */ final boolean casBase(long cmp, long val) { return UNSAFE.compareAndSwapLong(this, BASE, cmp, val); }
原子更新base没啥好说的,若是更新成功,本地调用开始返回,不然进入分支内部。高并发
何时会更新失败? 没错,并发的时候,好戏开始了,AtomicLong的处理方式是死循环尝试更新,直到成功才返回,而LongAdder则是进入这个分支。性能
分支内部,经过一个Threadlocal变量threadHashCode 获取一个HashCode对象,该HashCode对象依然是Striped64类的内部类,看定义:
/** * Returns the probe value for the current thread. * Duplicated from ThreadLocalRandom because of packaging restrictions. */ static final int getProbe() { return UNSAFE.getInt(Thread.currentThread(), PROBE); }
有个code变量,保存了一个非0的随机数随机值。
回到add方法:
拿到该线程相关的HashCode对象后,获取它的code变量,as[(n-1)&h] 这句话至关于对h取模,只不过比起取模,由于是 与 的运算因此效率更高。
计算出一个在Cells 数组中当先线程的HashCode对应的 索引位置,并将该位置的Cell 对象拿出来用CAS更新它的value值。
看到这里我想应该有不少人明白为何LongAdder会比AtomicLong更高效了,没错,惟一会制约AtomicLong高效的缘由是高并发,
高并发意味着CAS的失败概率更高, 重试次数更多,越多线程重试,CAS失败概率又越高,变成恶性循环,AtomicLong效率下降。
那怎么解决?
LongAdder给了咱们一个很是容易想到的解决方案:减小并发,将单一value的更新压力分担到多个value中去,下降单个value的 “热度”,分段更新!!!
这样,线程数再多也会分担到多个value上去更新,只须要增长value就能够下降 value的 “热度” AtomicLong中的 恶性循环不就解决了吗?
cells 就是这个 “段” cell中的value 就是存放更新值的, 这样,当我须要总数时,把cells 中的value都累加一下不就能够了么!!
固然,聪明之处远远不只仅这里,在看看add方法中的代码,casBase方法可不能够不要,直接分段更新,上来就计算 索引位置,而后更新value?
答案是很差,不是不行,由于,casBase操做等价于AtomicLong中的CAS操做,要知道,LongAdder这样的处理方式是有坏处的,分段操做必然带来空间上的浪费,
能够空间换时间,可是,能不换就不换,看空间时间都节约~! 因此,casBase操做保证了在低并发时,不会当即进入分支作分段更新操做,由于低并发时,
casBase操做基本都会成功,只有并发高到必定程度了,才会进入分支,
因此,Doug Lea对该类的说明是: 低并发时LongAdder和AtomicLong性能差很少,高并发时LongAdder更高效!
1. base有没有参与汇总?
base在调用intValue等方法的时候是会汇总
2. 若是cell被建立后,原来的casBase就不走了,会不会性能更差? base的顺序可不能够调换?
刚开始我想可不能够调换add方法中的判断顺序,好比,先作casBase的判断?
仔细思考后认为仍是不调换可能更好,调换后每次都要CAS一下,在高并发时,失败概率很是高,而且是恶性循环,比起一次判断,
后者的开销明显小不少,尚未反作用(上一个问题,base变量在sum时base是会被统计的,并不会丢掉base的值)。所以,不调换可能会更好。
3. AtomicLong可不能够废掉?
虽然LongAdder在空间上占用略大,可是,它的性能已经足以说明一切了,不管是从节约空的角度仍是执行效率上,AtomicLong基本没有优点了
4. guava 、netty里面都照搬了LongAdder的实现
出处: