Java—多线程之学了就要用-CountDownLatch

背景

最近在做的项目中有一个任务工作台功能,需要展示当前报表填报任务的总体进度,便于管理人员进行进度跟踪和整体感知。但是这个功能写完之后,当系统下发的报表任务较多时,任务工作台加载的就会很慢,大约5秒多才能完成加载。用户体验太差,正好这段时间在学习多线程相关的知识,就决定来优化一下。

改造过程

1、分析任务属于CPU密集型还是IO密集型

分清是计算(CPU)密集型任务还是IO密集型任务至关重要,直接关系到线程池的初始化参数的设置。

  • 对于计算密集型的任务

在拥有N个处理器的系统上,当线程池的大小为N+1时,通常能实现最优的效率。(即使当计算密集型的线程偶尔由于缺失故障或者其他原因而暂停时,这个额外的线程也能确保CPU的时钟周期不会被浪费。)

  • 对于IO密集型任务

事实上大部分的任务都是I/O密集型的,即大部分任务消耗集中在的输入输出(数据库数据交互、文件上传下载、网络数据传输等等)。线程中的任务最终是交给CPU的线程去处理的,而CPU可同时处理线程数量大部分是CPU核数的两倍,所以将线程池的核心池线程数量配置为CPU核数的两倍是比较合适的。

项目中的任务属于IO密集型,因为都是数据库操作,执行查询语句  。

运行环境中CPU的核数我们可以通过Runtime.getRuntime().availableProcessors()这个方来而获取。理论上来说核心池线程数量应该为Runtime.getRuntime().availableProcessors()*2

  • 确定线程池的核心池大小和最大值
// 获取CPU核心数
final int PROCESSORS = Runtime.getRuntime().availableProcessors();
// 判断当前要处理的任务数是否小于核心数
int corePoolSize = distList.size() < PROCESSORS ? distList.size():PROCESSORS;
ExecutorService pool = new ThreadPoolExecutor(corePoolSize*2, PROCESSORS*8,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(1024), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

2、对需要共享的变量使用Atomic替换

AtomicInteger finishedNum = new AtomicInteger(0);
AtomicInteger unfinishedNum =  new AtomicInteger(0);
AtomicInteger notStartedNum =  new AtomicInteger(0);

3、对结果集进行线程安全包装

List<Map<String,Object>> progressList  = Collections.synchronizedList(new ArrayList<Map<String,Object>>());

最终结果

1、最终代码

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public Map<String,Object> getRepProgress(){
        final int PROCESSORS = Runtime.getRuntime().availableProcessors();
        Map<String,Object> tjMap = new HashMap<String,Object>(16);
        List<Map<String,Object>> progressList  = Collections.synchronizedList(new ArrayList<Map<String,Object>>());
       
        List<Map<String,Object>> distList = reportDao.getDistRecordList();

        AtomicInteger finishedNum = new AtomicInteger(0);
        AtomicInteger unfinishedNum =  new AtomicInteger(0);
        AtomicInteger notStartedNum =  new AtomicInteger(0);
        CountDownLatch latch = new CountDownLatch(distList.size());

        int corePoolSize = distList.size() < PROCESSORS ? distList.size():PROCESSORS;

        ExecutorService pool = new ThreadPoolExecutor(corePoolSize*2, PROCESSORS*8,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(1024), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

        for (Map<String,Object> map:distList) {
            pool.execute(() -> {
                // ....业务代码

                // 填报单位总数
                long sum = 0;
                // 已上报单位数
                long ysbSl = 0;
      
                String status = "";
                if(ysbSl == 0){
                    // 未开始
                    status = "01";
                    notStartedNum.incrementAndGet();
                }else if(ysbSl == sum){
                    // 已完成
                    status = "03";
                    finishedNum.incrementAndGet();
                }else{
                    // 进行中
                    status = "02";
                    unfinishedNum.incrementAndGet();
                }
                Map<String,Object> progressMap = new HashMap<String,Object>(16);                
                progressMap.put("status",status);
                progressMap.put("sbqk",ysbSl+"/"+sum);
                progressMap.put("progress",Math.round((float) ysbSl/(float)sum*100));

                progressList.add(progressMap);
                latch.countDown();
            });
        }
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 关闭线程池
        pool.shutdown();
        System.out.println("计算任务处理完毕");

        tjMap.put("reportNum",distList.size());
        tjMap.put("unfinishedNum",unfinishedNum);
        tjMap.put("finishedNum",finishedNum);
        tjMap.put("notStartedNum",notStartedNum);
        tjMap.put("progressList",progressList);
        return tjMap;
    }

2、优化结果

  • 代码未优化前

执行时间为5408ms,平均下来也有50xxms

  • 代码优化后,引入多线程进行处理后

在4核处理器上运行,大概在1325ms上下

在8核处理器上运行,大约900ms上下

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值