LongAdder

LongAdder閱讀筆記

問題

1、爲什麼JDK8需要增加LongAdder?

  • 解決AtomicInteger在併發增加的情況下的性能下降問題

2、AtomicInteger的實現原理?

  • 通過cells數組和base來存儲數據
  • 不同的線程會hash到不同的cells或base

3、LongAdder和AtomicInteger的對比?

  • 單線程選AtomicInteger(不過都單線程了我不如直接用Integer。。。)
  • 併發數量遞增的情況下LongAdder受到的影響較小(可看總結的測試圖)

一、簡介

就是爲了解決併發的情況下的數據更新加速而已,如果不是併發更新太多,還是用Integer 或者AtomicInteger把。同時它採用的數組來解決線程併發問題,且數組最大數量和系統核等同

二、繼承關係圖

在這裏插入圖片描述

1、繼承Striped64Striped64繼承Number

2、實現Serializable接口

三、存儲結構

繼承的Striped64的結構

四、源碼分析

內部類

繼承的Striped64的結構

屬性

構造

public LongAdder(){}

主要方法

  • public void add(long x):元素添加

    • /**
           * Adds the given value.
           *
           * @param x the value to add
           */
      public void add(long x) {
          //as 是striped64中的cells屬性
          //b 是striped64中的base屬性
          //v 是當前線程hash到cells中存儲的值
          //m 是cells的長度-1,hash時做爲掩碼使用
          //a a 是當前線程從as中hash到的cell
          Cell[] as; long b, v; int m; Cell a;
          //cells不爲null 或 casBase成功
          if ((as = cells) != null || !casBase(b = base, b + x)) {
              boolean uncontended = true;
              //1、如果cells爲null
              //2、如果cells長度爲0
              //3、如果cells數組中當前線程爲null
              //4、如果直接修改當前線程的cell 失敗
              //1-4點任意一點符合條件,則調用Striped64.longAccumulate函數進行添加元素
              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);
          	}
          }
      }
      
  • public void increment() {add(1L);}:元素加1

  • public void decrement() {add(-1L);}:元素-1

  • public void reset():重置所有cells和base爲0

    • /**
           * Resets variables maintaining the sum to zero.  This method may
           * be a useful alternative to creating a new adder, but is only
           * effective if there are no concurrent updates.  Because this
           * method is intrinsically racy, it should only be used when it is
           * known that no threads are concurrently updating.
           */
      //次方法只有在沒有併發更新的情況下才有效,所以要做好規避
      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;
              }
          }
      }
      
  • public long sum():求和計算

    • /**
           * Returns the current sum.  The returned value is <em>NOT</em> an
           * atomic snapshot; invocation in the absence of concurrent
           * updates returns an accurate result, but concurrent updates that
           * occur while the sum is being calculated might not be
           * incorporated.
           *
           * @return the sum
           */ 
      //不是強一致性,但是可保證最終一致性
      //(如果沒有併發更新,將返回準確的結果,但是在合併時,併發更新的結果不會被合併)
      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;
      }
      
  • public long sumThenReset():求和然後重置(sum和reset的組合)

    • /**
           * Equivalent in effect to {@link #sum} followed by {@link
           * #reset}. This method may apply for example during quiescent
           * points between multithreaded computations.  If there are
           * updates concurrent with this method, the returned value is
           * <em>not</em> guaranteed to be the final value occurring before
           * the reset.
           *
           * @return the sum
           */
      //只可保證併發更新前的結果,而重置是不會被生效的
      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和AtomicInteger做性能比較,當只有一個線程時AtomicInteger要比LongAdder高,但是隨着線程的數量越來越多,Atomic性能急速下降,LongAdder的性能影響很小

  • 如下是參考老彤的測試數據
    在這裏插入圖片描述
    文章參考

發佈了48 篇原創文章 · 獲贊 17 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章