CompletableFuture异步编程

543 阅读3分钟

最近快大促了,各种报表啊,大屏啊等需求开始出来作妖,那来的是一个猝不及防,专门来打你一个措手不及。年年大促都要搞,一年还有搞两次,没办法,红牛、咖啡☕️、跨夜安排上,必须给他搞好,主打的就是一个客户为先。

各种单一接口的新增更改就不说了,没什么说的。说一说一个计算总量的接口,报表或者大屏嘛,肯定是统计各种不同类型的单量,然后在来一个总量的计算,要是计算总量的口径单一还好说,关键是不单一,我们主要是按照不同的商品品类来区分的,比如空调、卫浴、冰箱、洗衣机、电视等,每个类型都有单独的接口,按照自己的口径去统计,总量接口则需要将以上单一接口统计的单量汇总并进行一定加工处理,伪代码示例如下:

AllCountResponse countResponse = new AllCountResponse();

//空调
CompletableFuture<Result<List<CountAirResponse>>> airFuture = CompletableFuture.supplyAsync(this::countAir,qqdExecutorService);
//卫浴
CompletableFuture<Result<List<CountWashResponse>>> washFuture = CompletableFuture.supplyAsync(this::countWash,qqdExecutorService);
//冰箱
CompletableFuture<Result<List<CountFridgeResponse>>> fridgeFuture = CompletableFuture.supplyAsync(this::countFridge,qqdExecutorService);
//洗衣机
CompletableFuture<Result<List<CountMachineResponse>>> machineFuture = CompletableFuture.supplyAsync(this::countMachine,qqdExecutorService);
//电视
CompletableFuture<Result<List<CountTvResponse>>> tvFuture = CompletableFuture.supplyAsync(this::countTv,qqdExecutorService);
//清洗
CompletableFuture<Result<List<CountCleanResponse>>> cleanFuture = CompletableFuture.supplyAsync(this::countClean,qqdExecutorService);
//电子锁
CompletableFuture<Result<List<CountLockResponse>>> lockFuture = CompletableFuture.supplyAsync(this::countLock,qqdExecutorService);

//一系列独立的 `future`任务,等其所有的任务执行完后做一些事情.
CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(
        airFuture,washFuture,fridgeFuture,machineFuture,tvFuture,
        cleanFuture,lockFuture);

AllCountResponse finalCountResponse = countResponse;
CompletableFuture<AllCountResponse> resultFuture = allOfFuture.thenApply(v-> {

    try {
        //空调下传量/完成量
        Result<List<CountAirResponse>> airResult = airFuture.get(5, TimeUnit.SECONDS);
        Integer completeCountAir = 0;
        if (CollectionUtils.isNotEmpty(airResult.getData())) {
            CountAirResponse countAirResponse = airResult.getData().get(0);
            finalCountResponse.setInstallCount(airResponse.getSendCount());
            completeCountAir = countAirResponse.getCompleteCount();
        }

        //卫浴下传量
        Result<List<CountWashResponse>> washResult = washFuture.get(5, TimeUnit.SECONDS);

        if (CollectionUtils.isNotEmpty(washResult.getData())) {
            CountWashResponse countWashResponse = washResult.getData().get(0);
            finalCountResponse.setScFix(countWashResponse.getFixCount());
        }
        //冰箱下传量/完成量
        Result<List<CountFridgeResponse>> fridgeResult = fridgeFuture.get(5, TimeUnit.SECONDS);
        Integer completeCountFridge = 0;
        if (CollectionUtils.isNotEmpty(fridgeResult.getData())) {
            CountFridgeResponse countFridgeResponse = fridgeResult.getData().get(0);
            finalCountResponse.setScDoor(countFridgeResponse.getSendCount());
            completeCountFridge = countFridgeResponse.getCompleteCount();
        }

        //洗衣机下传量/完成量
        Result<List<CountMachineResponse>> machineResult = machineFuture.get(5, TimeUnit.SECONDS);
        Integer completeCountMachine = 0;
        if (CollectionUtils.isNotEmpty(machineResult.getData())) {
            CountMachineResponse countMachineResponse = machineResult.getData().get(0);
            finalCountResponse.setHomeElectricRepair(countMachineResponse.getSendCount());
            completeCountMachine = countMachineResponse.getCompleteCount();
        }
        //清洗下传量/完成量
        Result<List<CountCleanResponse>> cleanResult = cleanFuture.get(5, TimeUnit.SECONDS);
        
        Integer completeCountClean = 0;
        if (CollectionUtils.isNotEmpty(cleanResult.getData())) {
            CountCleanResponse countCleanResponse = cleanResult.getData().get(0);
            finalCountResponse.setToiletriesCount(countCleanResponse.getSendCount());
            completeCountClean = countCleanResponse.getCompleteCount();
        }
        //电视下传量/完成量
        Result<List<CountTvResponse>> tvResult = tvFuture.get(5, TimeUnit.SECONDS);
      
        Integer completeCountTv = 0;
        if (CollectionUtils.isNotEmpty(tvResult.getData())) {
            CountTvResponse countTvResponse = tvResult.getData().get(0);
            finalCountResponse.setWashCount(countTvResponse.getSendCount());
            completeCountTv = count4washResponse.getCompleteCount();
        }
        //电子锁下传量/完成量
        Result<List<CountLockResponse>> lockResult = lockFuture.get(5, TimeUnit.SECONDS);
      
        Integer completeCountLock = 0;
        if (CollectionUtils.isNotEmpty(lockResult.getData())) {
            CountLockResponse countLockResponse = lockResult.getData().get(0);
            finalCountResponse.setWashCount(countLockResponse.getSendCount());
            completeCountLock = countLockResponse.getCompleteCount();
        }
       
        //总量
        finalCountResponse.setAllCount((finalCountResponse.getInstallCount() == null ? 0 : finalCountResponse.getInstallCount()) +
                (finalCountResponse.getScFix() == null ? 0 : finalCountResponse.getScFix()) +
                (finalCountResponse.getScDoor() == null ? 0 : finalCountResponse.getScDoor()) +
                (finalCountResponse.getWashCount() == null ? 0 : finalCountResponse.getWashCount()) +
                (finalCountResponse.getToiletriesCount() == null ? 0 : finalCountResponse.getToiletriesCount()) +
                (finalCountResponse.getHomeElectricRepair() == null ? 0 : finalCountResponse.getHomeElectricRepair())
        );

        // 完成量
        finalCountResponse.setCompleteCount(completeCountAir +
                completeCountFridge +
                completeCountMachine +
                completeCountClean +
                completeCountLock + completeCountTv);


        return finalCountResponse;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
});
countResponse = resultFuture.join();

其中涉及到的一些方法介绍如下:

CompletableFuture.supplyAsync() 创建有返回值的简单异步任务,在上面的代码中函数第二个参数表示是用我们自己创建的线程池,否则采用默认的ForkJoinPool.commonPool(),建议使用自定义,资源隔离,否则核心和非核心业务会竞争同一个池中的线程,容易出现系统瓶颈。

thenApply 依赖上一个任务的结果,有参、有返回值

CompletableFuture.allOf 一系列独立的 future 任务,等其所有的任务执行完后做一些事情,当给定的所有CompletableFuture都完成时,返回一个新的CompletableFuture

附件

在项目中只使用到了其中的一种场景,CompletableFuture依然有很多场景等着被挖掘,具体的可以参考一下几篇博客:

CompletableFuture 异步编排、案例及应用小案例

Java 8 CompletableFuture 异步编程

CompletableFuture实现异步编排全面分析和总结