我们异步执行一个任务时,一般是用线程池Executor去创建。如果不需要有返回值, 任务实现Runnable接口;如果需要有返回值,任务实现Callable接口,调用Executor的submit方法,再使用Future获取即可。如果多个线程存在依赖组合的话,我们怎么处理呢?可使用同步组件CountDownLatch、CyclicBarrier等,但是比较麻烦。其实有简单的方法,就是用CompeletableFuture。最近刚好使用CompeletableFuture优化了项目中的代码,所以跟大家一起学习CompletableFuture。
CompletableFuture创建异步任务,一般有supplyAsync和runAsync两个方法
- supplyAsync执行CompletableFuture任务,支持返回值
- runAsync执行CompletableFuture任务,没有返回值。
CompletableFuture的supplyAsync方法,提供了异步执行的功能,线程池也不用单独创建了。实际上,它CompletableFuture使用了默认线程池是ForkJoinPool.commonPool。
优化前代码 依次执行 时间依次累加,效率低下
使用CompletableFuture优化
/**
* 获取首页基本数据
* @param beginCreateTime
* @param endCreateTime
* @return
*/
@Override
public IndexBaseInfoVO getBaseInfo(String beginCreateTime, String endCreateTime) {
//1)构建一个空的结果集对象
IndexBaseInfoVO result = new IndexBaseInfoVO();
//2 封装结果集属性
// 2.1 由于查询需要用到用户名 调用工具类获取用户名
String username = SecurityUtils.getUsername();
try {
CompletableFuture<Integer> clueNums = CompletableFuture.supplyAsync(()->{
// 2.2 开始查询第一个属性 线索数量
return reportMpper.getCluesNum(beginCreateTime, endCreateTime, username);
});
CompletableFuture<Integer> bussinessNum = CompletableFuture.supplyAsync(()->{
// 2.3 开始查询第一个属性 商机数量
return reportMpper.getBusinessNum(beginCreateTime, endCreateTime, username);
});
CompletableFuture<Integer> contractNum = CompletableFuture.supplyAsync(()->{
// 2.4 开始查询第一个属性 合同数量
return reportMpper.getContractNum(beginCreateTime, endCreateTime, username);
});
CompletableFuture<Double> saleAmount = CompletableFuture.supplyAsync(()->{
// 2.5 开始查询第一个属性 销售金额数量
return reportMpper.getSalesAmount(beginCreateTime, endCreateTime, username);
});
//3 join等待所有线程全部执行完成
CompletableFuture
.allOf(clueNums,
bussinessNum,
contractNum,
saleAmount)
.join();
//4 封装结果集对象
result.setCluesNum(clueNums.get());
result.setBusinessNum(bussinessNum.get());
result.setContractNum(contractNum.get());
result.setSalesAmount(saleAmount.get());
}catch (Exception e) {
e.printStackTrace();
return null;
}
//5 返回结果集对象
return result;
}