LongAdder閱讀筆記
問題
1、爲什麼JDK8需要增加LongAdder?
- 解決AtomicInteger在併發增加的情況下的性能下降問題
2、AtomicInteger的實現原理?
- 通過cells數組和base來存儲數據
- 不同的線程會hash到不同的cells或base
3、LongAdder和AtomicInteger的對比?
- 單線程選AtomicInteger(不過都單線程了我不如直接用Integer。。。)
- 併發數量遞增的情況下LongAdder受到的影響較小(可看總結的測試圖)
一、簡介
就是爲了解決併發的情況下的數據更新加速而已,如果不是併發更新太多,還是用Integer 或者AtomicInteger把。同時它採用的數組來解決線程併發問題,且數組最大數量和系統核等同
二、繼承關係圖
1、繼承Striped64,Striped64繼承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的性能影響很小
-
如下是參考老彤的測試數據
文章參考