欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

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

发布时间:2023/12/3 编程问答 45 豆豆
生活随笔 收集整理的这篇文章主要介绍了 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


    总结

    以上是生活随笔为你收集整理的Jdk1.8 JUC源码增量解析(2)-atomic-LongAdder和LongAccumulator的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。