• 【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 中,添加元素后对集合大小累加时,也是这样方案)

  • 相关阅读:
    MyISAM表锁的解决方案
    RSA数字证书管理
    Self Host WebApi服务传输层SSL加密(服务器端+客户端调用)
    WebApi服务Uri加密及验证的两种方式
    利用MVC的自定义过滤器FilterAttribute、IActionFilter、IExceptionFilter实现异常处理等功能
    html页面中meta的作用
    [转]REST简介
    [转]webApi 参数传递总结
    REST服务中的异常处理
    REST服务返回自定义的HttpResponseMessage
  • 原文地址:https://www.cnblogs.com/h--d/p/14595141.html
Copyright © 2020-2023  润新知