【Java多線程】線程最快累加方案(二十三)


線程最快累加方案

  學習多線程期間,想了一個問題,多個線程累計時,怎樣才能使計算又快又安全?

  問題:一個初始值為0的變量,10個線程對其進行累計,一個線程對其累加 100_000_000 次,每次加2,請求結果及耗時短的方案?

  四種方案,如下:

 1 import java.util.concurrent.BrokenBarrierException;
 2 import java.util.concurrent.CountDownLatch;
 3 import java.util.concurrent.atomic.AtomicInteger;
 4 import java.util.concurrent.atomic.LongAccumulator;
 5 import java.util.concurrent.atomic.LongAdder;
 6 
 7 public class FastAccumulator {
 8 
 9     public static int THREAD_NUM = 10;
10     public static int COUNT_NUM = 100_000_000;
11     public static CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
12 
13     // 方案一:使用 普通遍歷 + synchronized關鍵字
14     public static Integer count = 0;
15     // 方案二:使用 AtomicInteger 並發原子類
16 //    public static AtomicInteger count = new AtomicInteger();
17     // 方案三:使用 juc包中的 LongAdder
18 //    public static LongAdder count = new LongAdder();
19     // 方案四:使用 juc包中的 LongAdder
20 //    public static LongAccumulator count = new LongAccumulator((left, right) -> left + right,0);
21 
22     public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
23 
24         long start = System.currentTimeMillis();
25         for (int i = 0; i < THREAD_NUM; i++)
26             new Thread(() -> {
27                 for (int j = 0; j < COUNT_NUM; j++) {
28                     // 方案一
29                     synchronized (FastAccumulator.class) {
30                         count += 2;
31                     }
32                     // 方案二
33 //                    count.addAndGet(2);
34                     // 方案三
35 //                    count.add(2);
36                     // 方案四
37 //                    count.accumulate(2);
38 
39                 }
40                 countDownLatch.countDown();
41             }).start();
42         countDownLatch.await();
43         long end = System.currentTimeMillis();
44         System.out.println(count);
45         System.out.println("耗時:" + (end - start));
46     }
47 }

  結果如下:

 1 // 方案一
 2 2000000000
 3 耗時:47515
 4 
 5 // 方案一
 6 2000000000
 7 耗時:46455
 8 
 9 // 方案二
10 2000000000
11 耗時:22224
12 
13 // 方案二
14 2000000000
15 耗時:20618
16 
17 // 方案二
18 2000000000
19 耗時:20098
20 
21 // 方案三
22 2000000000
23 耗時:5094
24 
25 // 方案三
26 2000000000
27 耗時:4751
28 
29 // 方案三
30 2000000000
31 耗時:4909
32 
33 // 方案四
34 2000000000
35 耗時:4938
36 
37 // 方案四
38 2000000000
39 耗時:4679
40 
41 // 方案四
42 2000000000
43 耗時:5009

  結果:

    耗時由長到短:synchronized 關鍵字 > AtomicInteger 類 > LongAdder, LongAccumulator 類

    可以看出使用 LongAdder 和 LongAccumulator 耗時最短。

  原因:

    方案一:synchronized 關鍵字,代碼運行會將鎖升級到重量級鎖,比較耗時

    方案二:AtomicInteger 類,在內存中使用CAS自旋累加,但是加的結果都指向內存中的一個變量,沖突會比較嚴重,耗時較多

    方案三: LongAdder 和 LongAccumulator 底層使用了 base(基本值)+ Cell[] cells(單元表)來保持數據,當需要進行累加一個值 n 時,根據線程的一個特有值計算得到對應的cells[i],執行 cells[i] = cells[i] + n,如果沖突可以對 cells 擴容,或者把值 n 累加到 base 上,這樣就有多個變量可以進行累加,最后求和是只要把 base 和 cells 數組中的值都加起來即可。(ConcurrentHashMap 中,添加元素后對集合大小累加時,也是這樣方案)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM