Jdk1.8 JUC源码增量解析(2)-atomic-LongAdder和LongAccumulator

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

 

 

 

 

你可能感兴趣的:(java,源码,并发,juc)