转载自 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一样。
代码解析完毕!
参见:Jdk1.8 JUC源码增量解析(1)-atomic-Striped64
参见:Jdk1.6 JUC源码解析(1)-atomic-AtomicXXX