[JDK8]性能优化之使用LongAdder替换AtomicLong
如果讓你實現一個計數器,有點經驗的同學可以很快的想到使用AtomicInteger或者AtomicLong進行簡單的封裝。
因為計數器操作涉及到內存的可見性和線程之間的競爭,而Atomic***的實現完美的屏蔽了這些技術細節,我們只需要執行相應的方法,就能實現對應的業務需求。
Atomic**雖然好用,不過這些的操作在并發量很大的情況下,性能問題也會被相應的放大。我們可以先看下其中getAndIncrement的實現代碼
public final long getAndIncrement() {return unsafe.getAndAddLong(this, valueOffset, 1L); }// unsafe類中的實現 public final long getAndAddLong(Object var1, long var2, long var4) {long var6;do {var6 = this.getLongVolatile(var1, var2);} while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));return var6; }很顯然,在getAndAddLong實現中,為了實現正確的累加操作,如果并發量很大的話,cpu會花費大量的時間在試錯上面,相當于一個spin(自旋)的操作。如果并發量小的情況,這些消耗可以忽略不計。
既然已經意識到Atomic***有這樣的業務缺陷,Doug Lea大神又給我們提供了LongAdder,內部的實現有點類似ConcurrentHashMap的分段鎖,最好的情況下,每個線程都有獨立的計數器,這樣可以大量減少并發操作。
下面通過JMH比較一下AtomicLong 和 LongAdder的性能。
@OutputTimeUnit(TimeUnit.MICROSECONDS) @BenchmarkMode(Mode.Throughput) public class Main {private static AtomicLong count = new AtomicLong();private static LongAdder longAdder = new LongAdder();public static void main(String[] args) throws Exception {Options options = new OptionsBuilder().include(Main.class.getName()).forks(1).build();new Runner(options).run();}@Benchmark@Threads(10)public void run0(){count.getAndIncrement();}@Benchmark@Threads(10)public void run1(){longAdder.increment();} }1、設置BenchmarkMode為Mode.Throughput,測試吞吐量
2、設置BenchmarkMode為Mode.AverageTime,測試平均耗時
?
線程數為1
1、吞吐量
Benchmark Mode Cnt Score Error Units Main.run0 thrpt 5 154.525 ± 9.767 ops/us Main.run1 thrpt 5 89.599 ± 7.951 ops/us2、平均耗時
Benchmark Mode Cnt Score Error Units Main.run0 avgt 5 0.007 ± 0.001 us/op Main.run1 avgt 5 0.011 ± 0.001 us/op單線程情況:
1、AtomicLong的吞吐量和平均耗時都占優勢
?
線程數為10
1、吞吐量
Benchmark Mode Cnt Score Error Units Main.run0 thrpt 5 37.780 ± 1.891 ops/us Main.run1 thrpt 5 464.927 ± 143.207 ops/us2、平均耗時
Benchmark Mode Cnt Score Error Units Main.run0 avgt 5 0.290 ± 0.038 us/op Main.run1 avgt 5 0.021 ± 0.001 us/op并發線程為10個時:
- LongAdder的吞吐量比較大,是AtomicLong的10倍多。
- LongAdder的平均耗時是AtomicLong的十分之一。
?
線程數為30
1、吞吐量 Benchmark Mode Cnt Score Error Units Main.run0 thrpt 5 36.215 ± 2.341 ops/us Main.run1 thrpt 5 486.630 ± 26.894 ops/us2、平均耗時
Benchmark Mode Cnt Score Error Units Main.run0 avgt 5 0.792 ± 0.021 us/op Main.run1 avgt 5 0.063 ± 0.002 us/op線程數為30個時:
- LongAdder的吞吐量比較大,也是AtomicLong的10倍多。
- LongAdder的平均耗時也是AtomicLong的十分之一。
總結
一些高并發的場景,比如限流計數器,建議使用LongAdder替換AtomicLong,性能可以提升不少。
轉載于:https://www.cnblogs.com/Joy-Hu/p/10715682.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的[JDK8]性能优化之使用LongAdder替换AtomicLong的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Vue 组件间通信方法汇总
- 下一篇: 学习python第四天内容回顾