一、CompletionService 原理
内部通过阻塞队列+FutureTask,实现了任务先完成可优先获取到,即结果按照完成先后顺序排序,内部有一个先进先出的阻塞队列,用于保存已经执行完成的Future,通过调用它的take方法或poll方法可以获取到一个已经执行完成的Future,进而通过调用Future接口实现类的get方法获取最终的结果
- 并行地调用多个服务实例,只要有一个成功就返回结果
场景:判断用户是否有购买过权益卡(共9种类型权益卡)。只要购买过其中一种就算购买。
@Autowired
@Qualifier(ThreadPoolConfig.CBS_IO_EXECUTOR)
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
public ResultMessage<Boolean> haveSucessRightOrder(String custNo) {
// 创建CompletionService
CompletionService<Integer> cs = new ExecutorCompletionService<>(threadPoolTaskExecutor);
// 用于保存Future对象,共9个查询任务
List<Future<Integer>> futures = new ArrayList<>(9);
//提交异步任务,并保存future到futures
futures.add(cs.submit(() -> {
List<CardOrderV0> cardOrderV0s = advServiceClient.pageEnjoycard(custNo, com.smy.ibs.enums.OrderStatus.pay_suc.getCode());
return Optional.ofNullable(cardOrderV0s).map(k -> k.size()).orElse(0);
}));
futures.add(cs.submit(() -> {
List<TEnjoymentCardOrder> tEnjoymentCardOrders = advServiceClient.queryEnjoymentCards(custNo);
return Optional.ofNullable(tEnjoymentCardOrders).map(k -> k.size()).orElse(0);
}));
futures.add(cs.submit(() -> {
List<IbsInsuranceBorrowerOrder> orders = advServiceClient.getInsuranceOrdersByParam(custNo, 3);
return Optional.ofNullable(orders).map(k -> k.size()).orElse(0);
}));
futures.add(cs.submit(() -> {
List<TSaveMoneyCardOrder> saveMoneyCardOrders = cbsSaveMoneyCardService.getOrders(SaveMoneyCardOrderOmsReq.builder().custNo(custNo).orderStatus(OrderStatus.PAY.getCode()).start(0).length(1).build());
return Optional.ofNullable(saveMoneyCardOrders).map(k -> k.size()).orElse(0);
}));
futures.add(cs.submit(() -> {
List<VipPayOrderOmsResp> xshkOrders = cbsVipUserXshkService.getPayOrderRecs(VipPayOrderOmsReq.builder().custNo(custNo).orderStatus(OrderStatus.PAY.getCode()).start(0).length(1).build());
return Optional.ofNullable(xshkOrders).map(k -> k.size()).orElse(0);
}));
futures.add(cs.submit(() -> {
VipOwnOrderRequst requst = new VipOwnOrderRequst();
requst.setOrderStatus(OrderStatus.PAY.getCode());
requst.setCustNo(custNo);
ResultMessage<List<VipOwnOrderResponse>> myOrders = cbsVipUserOwnService.getMyOrders(requst);
if (!myOrders.getResult()) {
return 0;
}
return Optional.ofNullable(myOrders.getData()).map(k -> k.size()).orElse(0);
}));
futures.add(cs.submit(() -> {
GetMoneyCardOrderOmsReq req = new GetMoneyCardOrderOmsReq();
req.setCustNo(custNo);
req.setOrderStatus(OrderStatus.PAY.getCode());
List<GetMoneyCardOrderOmsResp> resps = cbsGetMoneyCardService.queryGetMoneyCardOrders(req);
return Optional.ofNullable(resps).map(k -> k.size()).orElse(0);
}));
futures.add(cs.submit(() -> {
List<TQuickenCardOrder> cardOrderRecs = cbsQuickenCardService.getCardOrderRecs(OmsCardOrderReq.builder().custNo(custNo).orderStatus(OrderStatus.PAY.getCode()).start(0).length(1).build());
return Optional.ofNullable(cardOrderRecs).map(k -> k.size()).orElse(0);
}));
futures.add(cs.submit(() -> {
CardOrderDto cardOrderDto = new CardOrderDto();
cardOrderDto.setCustNo(custNo);
cardOrderDto.setOrderStatus(OrderStatus.PAY.getCode());
ResultMessage<List<CardOrderExtend>> resultMessage = cbsCardService.queryCardOrders(cardOrderDto);
if (!resultMessage.getResult()) {
return 0;
}
return Optional.ofNullable(resultMessage.getData()).map(k -> k.size()).orElse(0);
}));
// 获取最快返回的任务执行结果
Integer r = 0;
try {
// 只要有一个成功返回,则break
for (int i = 0; i < 9; ++i) {
try {
r = cs.take().get();
} catch (Exception e) {
log.error("客户号在执行任务出现异常custNo:{},异常e:{}", custNo, e);
}
//通过判空和查询结果是否大于0来检查是否成功返回
if (r != null && r > 0) {
break;
}
}
} finally {
//取消所有任务
for (Future<Integer> f : futures)
f.cancel(true);
}
return ResultMessage.success(r > 0);
}
二、CompletableFuture
CompletableFuture是Future接口的扩展和增强。CompletableFuture实现了Future接口,并在此基础上进行了丰富地扩展,完美地弥补了Future上述的种种问题。更为重要的是,CompletableFuture实现了对任务的编排能力
- 并行地调用多个服务实例,并获取到所有处理结果
//获取短链后缀
Map<String, String> shorLinkMap = new HashMap<>();
Lists.partition(linkList, groupSize).stream()
.map(k -> CompletableFuture.supplyAsync(() -> this.getLinkMap(k), threadPoolTaskExecutor))
.map(CompletableFuture::join).filter(MapUtils::isNotEmpty).forEach(k -> shorLinkMap.putAll(k));
注意:这里不管是CompletionService 还是 CompletableFuture,我们都没有使用系统默认的线程池,而是用的自己定义好并Autowired注入的线程池。
原因是:
- 如果所有 CompletableFuture 共享一个线程池,那么一旦有任务执行一些很慢的 I/O 操作,就会导致线程池中所有线程都阻塞在 I/O 操作上,从而造成线程饥饿,进而影响整个系统的性能
- 自定义线程池中,可以设置线程名,可以根据名字区分不同的业务场景,做到场景隔离。
所以,强烈建议你要根据不同的业务类型创建不同的线程池,以避免互相干扰。下一章我们来讲解下线程池~