Jdk1.8 JUC源码增量解析(2)-atomic-LongAdder和LongAccumulator
作者:大飞
功能简介:
- LongAdder是jdk1.8提供的累加器,基于Striped64实现。它常用于状态采集、统计等场景。AtomicLong也可以用于这种场景,但在线程竞争激烈的情况下,LongAdder要比AtomicLong拥有更高的吞吐量,但会耗费更多的内存空间。
- LongAccumulator和LongAdder类似,也基于Striped64实现。但要比LongAdder更加灵活(要传入一个函数接口),LongAdder相当于是LongAccumulator的一种特例。
源码分析:
- 先看一下LongAdder类,看下结构:
public class LongAdder extends Striped64 implements Serializable { private static final long serialVersionUID = 7249069246863182397L; /** * Creates a new adder with initial sum of zero. */ public LongAdder() { }
LongAdder继承了Striped64,本身没有任何域。
- 再看一下LongAdder的方法:
public void add(long x) { Cell[] as; long b, v; int m; Cell a; //如果cell表为null,会尝试将x累加到base上。 if ((as = cells) != null || !casBase(b = base, b + x)) { /* * 如果cell表不为null或者尝试将x累加到base上失败,执行以下操作。 * 如果cell表不为null且通过当前线程的probe值定位到的cell表中的Cell不为null。 * 那么尝试累加x到对应的Cell上。 */ boolean uncontended = true; if (as == null || (m = as.length - 1) < 0 || (a = as[getProbe() & m]) == null || !(uncontended = a.cas(v = a.value, v + x))) //或者cell表为null,或者定位到的cell为null,或者尝试失败,都会调用下面的Striped64中定义的longAccumulate方法。 longAccumulate(x, null, uncontended); } }
add方法逻辑很简单,先尝试将x累加到base上,失败的话再看看能不能从cell表中找到cell,找到的话再尝试将x累加到这个cell里面,还失败的话就调用longAccumulate方法,这个方法上篇分析Striped64的时候分析过。
/** * Equivalent to {@code add(1)}. */ public void increment() { add(1L); } /** * Equivalent to {@code add(-1)}. */ public void decrement() { add(-1L); }
递增和递减方法,不需要解释了。
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; }
sum方法就是获取当前LongAdder值的总和,包括base和cells value两部分。
public void reset() { Cell[] as = cells; Cell a; base = 0L; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) a.value = 0L; } } }
重置方法,将base和cells value两部分值都置为0。
public long sumThenReset() { Cell[] as = cells; Cell a; long sum = base; base = 0L; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) { sum += a.value; a.value = 0L; } } } return sum; }
获取总和后重置。
LongAdder间接继承了Number,看下相关的方法实现:
public long longValue() { return sum(); } public int intValue() { return (int)sum(); } public float floatValue() { return (float)sum(); } public double doubleValue() { return (double)sum(); }
LongAdder的序列化使用序列化代理模式:
private static class SerializationProxy implements Serializable { private static final long serialVersionUID = 7249069246863182397L; private final long value; SerializationProxy(LongAdder a) { value = a.sum(); } private Object readResolve() { LongAdder a = new LongAdder(); a.base = value; return a; } } private Object writeReplace() { return new SerializationProxy(this); } private void readObject(java.io.ObjectInputStream s) throws java.io.InvalidObjectException { throw new java.io.InvalidObjectException("Proxy required"); }
- 再看一下LongAccumulator类,先看结构:
public class LongAccumulator extends Striped64 implements Serializable { private static final long serialVersionUID = 7249069246863182397L; private final LongBinaryOperator function; private final long identity; public LongAccumulator(LongBinaryOperator accumulatorFunction, long identity) { this.function = accumulatorFunction; base = this.identity = identity; }
LongAccumulator和LongAdder不同,内部有一个函数接口和一个初始值。
- 再看LongAccumulator的方法:
public void accumulate(long x) { Cell[] as; long b, v, r; int m; Cell a; if ((as = cells) != null || (r = function.applyAsLong(b = base, x)) != b && !casBase(b, r)) { boolean uncontended = true; if (as == null || (m = as.length - 1) < 0 || (a = as[getProbe() & m]) == null || !(uncontended = (r = function.applyAsLong(v = a.value, x)) == v || a.cas(v, r))) longAccumulate(x, function, uncontended); } }
和LongAdder的add方法一样的逻辑。
public long get() { Cell[] as = cells; Cell a; long result = base; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) result = function.applyAsLong(result, a.value); } } return result; }
将内部所有的零散值通过函数算出一个最终值。
public void reset() { Cell[] as = cells; Cell a; base = identity; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) a.value = identity; } } } public long getThenReset() { Cell[] as = cells; Cell a; long result = base; base = identity; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) { long v = a.value; a.value = identity; result = function.applyAsLong(result, v); } } } return result; }
注意这里和LongAdder不同,这里的重置会将base和cells value都重置成初始值-identity。
其他的Number方法和序列化方式和LongAdder一样。
代码解析完毕!