代码优化中关于CountDownLatch和CompletableFuture的使用

作者: admin 分类: JAVA 发布时间: 2020-12-09 15:14  阅读: 2,003 views

背景

做一个功能,在有一批的数据的情况下,处理 内容–>图片,图片–>上传的逻辑。
一个数据集大概几百条数据。需求是可能同时处理几十、几百个数据集。在处理完数据后,需要做汇总处理。

过程

先撸代码在说,一通流水账下来,功能测试正常。就来查看效率了。每次测试相同的数据,几百条记录而已。

初版: 18s

就是基本的for循环,处理完一条记录在去处理下一条。程序本身数据没有出错,就是效率比较慢。

...
for(FileCompareRecordDTO dto : request) {
        try {
            //如果有
            if(!map.containsKey(dto.getFileId())) { 
                String imageUrl = mathMlUtil.downMathMLToImage(changeMathMlStyle(dto.getMathml()));
                String img = mathMlUtil.buildMathML2Img(dto.getMathml(), imageUrl);
                map.put(dto.getFileId(), img);
            }
        }catch(Exception e) {
            log.info("");
        }
        cdl.countDown();
    questionIds.add(dto.getQuestionId());
    taskId = dto.getTaskId();
}
...

思考

异步处理:因为每次要拿到处理的结果,直接异步肯定不行。
多线程做:尽量保证多个线程的执行时间小于等于单线程的最大时间。

二版:8~9s

因为需要做汇总处理,那首先想到的是使用CountDownLatch工具类。速度是快了一些,但是并没有那么理想。
代码如下:

...
executorService 为自定义线程池

Long taskId = null;
HashMap<Long,String> map = new HashMap<Long,String>();
List<Long> questionIds = new ArrayList<Long>();
CountDownLatch cdl = new CountDownLatch(request.size());
for(FileCompareRecordDTO dto : request) {
    executorService.execute(() -> {
        try {
                String imageUrl = mathMlUtil.downMathMLToImage(changeMathMlStyle(dto.getMathml()));
                String img = mathMlUtil.buildMathML2Img(dto.getMathml(), imageUrl);
                map.put(dto.getFileId(), img);
        }catch(Exception e) {
            log.info("");
        }
        cdl.countDown();
    });
    questionIds.add(dto.getQuestionId());
    taskId = dto.getTaskId();
}
try {
    cdl.await(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
    log.error("处理信息出错 taskId:{},:{}",taskId,e);
}
...

终版: 2~3s

经新人指点,原来 jdk1.8之后提供了一个功能强大的类,支持异步回调,且线程并行。实际的测试效果要比 CountDownLatch更加明显。后浪果然厉害

executorService 为自定义线程池

List<CompletableFuture<FileUploadDO>> futureList=  request.stream().map(dto ->  CompletableFuture.supplyAsync(() -> doUploadFile(dto.getFileId(),dto.getMathml()), executorService )).collect(Collectors.toList());

CompletableFuture<Void> completableFuture=CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]));
completableFuture.join();

Map<Long, String> map = new HashMap<Long,String>();
log.info("futureList get info taskId:{},example:{}",taskId,JSON.toJSONString(futureList.get(0)));
try {
    map = futureList.stream().map(y -> {
        try {
            return y.get();
        } catch ( Exception e) {
            log.error("replaceQuestionMark method", e);
        }
        return new FileUploadDO();
    }).collect(Collectors.toMap(FileUploadDO::getFileId, FileUploadDO::getImg, (k1, k2) -> k1));
    log.info("replaceQuestionMark check taskId:{},map:{}",taskId, JSON.toJSONString(map.keySet()));
} catch ( Exception e) {
    log.error("map method", e);
}

//处理上传文件
private FileUploadDO doUploadFile(Long fileId, String mathml) {
        FileUploadDO fileUploadDO = new FileUploadDO();
        try {

            String imageUrl = mathMlUtil.downMathMLToImage(changeMathMlStyle(mathml));
            String img = mathMlUtil.buildMathML2Img(mathml, imageUrl);
            fileUploadDO.setFileId(fileId);
            fileUploadDO.setImg(img);
            return fileUploadDO;

        } catch (Exception e) {
            log.info("doUploadFile error,fileId={},mathml={}", fileId, mathml, e);
        }
        return fileUploadDO;
    }

最终还是要看业务场景的需要,看看jdk本身提供的那些工具类最好用。合适自己的才是最好的


   原创文章,转载请标明本文链接: 代码优化中关于CountDownLatch和CompletableFuture的使用

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

2条评论
  • 木心

    2020年12月22日 18:57

    可以试下reactor

    1. admin

      2020年12月23日 09:21

      可以试试.

发表评论

电子邮件地址不会被公开。 必填项已用*标注