多线程并发编排CompletableFuture分解+代码实践
原创1.从问题开始
假设在编写购物网站时,用户点击查看商品,我们的后端必须向用户显示商品的所有多样化维度信息(专业名称应为sku和spu),如果我们以单线程方式进行,逐步串行查询商品信息,ServiceImpl图层查询代码如下:
@Override //SkuInfoServiceImpl @TableName("pms_sku_info")
public SkuItemVo item(Long skuId) {
SkuItemVo skuItemVo = new SkuItemVo();
//1、sku基本信息的获取 查pms_sku_info表
SkuInfoEntity skuInfoEntity = this.getById(skuId);
skuItemVo.setInfo(skuInfoEntity);
Long spuId = skuInfoEntity.getSpuId();
Long catalogId = skuInfoEntity.getCatalogId();
//2、sku图片信息 查pms_sku_images表
List skuImagesEntities = skuimagesService.list(new QueryWrapper().eq("sku_id", skuId));
skuItemVo.setimages(skuimagesEntities);
//3、获取spu销售属性的组合。-> 需要依赖第一个一个一个1请求返回值的步骤。spuId
List saleAttrVos=skuSaleAttrValueService.listSaleAttrs(spuId);
skuItemVo.setSaleAttr(saleAttrVos);
//4、获取spu的介绍-> 需要依赖第一个一个一个1请求返回值的步骤。spuId
SpuInfoDescEntity byId = spuInfoDescService.getById(spuId);
skuItemVo.setDesc(byId);
//5、获取spu规格参数信息-> 需要依赖第一个一个一个1请求返回值的步骤。spuId和catalogId
List spuItemAttrGroupVos=productAttrValueService.getProductGroupAttrsBySpuId(spuId, catalogId);
skuItemVo.setGroupAttrs(spuItemAttrGroupVos);
//TODO 6,秒杀商品优惠信息
return skuItemVo;
}
这种循序渐进的调查没有问题,但速度肯定会很慢。如何解决这个问题?答案是使用多线程异步编排。CompletableFuture类。
二、CompletableFuture类介绍
在Java 8中, 一个新的包含50围绕方法类: CompletableFuture,提供了非常强大的异步编程功能。它通过函数编程以回调方式处理计算,并提供转换和组合等方法。
CompletableFuture 提供了四种静态方法来创建异步操作。
runAsync方法不支持返回值。
supplyAsync可以支持返回值。
static CompletableFuture runAsync(Runnable runnable)
public static CompletableFuture runAsync(Runnable runnable, Executor executor)
public static CompletableFuture supplyAsync(Supplier supplier)
public static CompletableFuture supplyAsync(Supplier supplier, Executor executor)
注:未指定Executor方法将使用ForkJoinPool.commonPool() 执行异步代码作为其线程池。如果指定了线程池,则使用指定的线程池运行。我们尽力使用自己的线程池。如何创建和配置线程池,请参阅本专栏的最后一篇文章“线程池实战分析”+完整代码。
CompletableFuture 提供了三种类型的线程序列化方法。
thenApply 方法:当一个线程依赖于另一个线程时,它会得到前一个任务返回的结果,并返回当前任务的返回值。
thenAccept方法:消耗处理结果。接收任务的处理结果,并在不返回结果的情况下使用处理。
thenRun方法:只要完成上述任务,就将开始执行。thenRun,在处理完任务后,执行 thenRun后续操作。
public CompletableFuture thenApply(Function super T,? extends U> fn)
public CompletableFuture thenApplyAsync(Function super T,? extends U> fn)
public CompletableFuture thenApplyAsync(Function super T,? extends U> fn, Executor executor)
public CompletionStage thenAccept(Consumer super T> action);
public CompletionStage thenAcceptAsync(Consumer super T> action);
public CompletionStage thenAcceptAsync(Consumer super T> action,Executor executor);
public CompletionStage thenRun(Runnable action);
public CompletionStage thenRunAsync(Runnable action);
public CompletionStage thenRunAsync(Runnable action,Executor executor);
注:Async默认值为异步。这里的异步是指不在当前线程内执行。
3.多线程异步业务流程
货物的详细信息是多个sql,而有些步骤需要使用第一步查询的结果,因此通过异步排列,优化上述购物网站商品详情代码。
@Override // SkuInfoServiceImpl
public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {
SkuItemVo skuItemVo = new SkuItemVo();
CompletableFuture infoFutrue = CompletableFuture.supplyAsync(() -> {
//1 sku基本信息
SkuInfoEntity info = getById(skuId);
skuItemVo.setInfo(info);
return info;
}, executor);
// 无需获取返回值
CompletableFuture imageFuture = CompletableFuture.runAsync(() -> {
//2 sku图片信息
List images = imagesService.getImagesBySkuId(skuId);
skuItemVo.setImages(images);
}, executor);
// 在1之后
CompletableFuture saleAttrFuture =infoFutrue.thenAcceptAsync(res -> {
//3 获取spu销售属性组合 list
List saleAttrVos = skuSaleAttrValueService.getSaleAttrsBuSpuId(res.getSpuId());
skuItemVo.setSaleAttr(saleAttrVos);
},executor);
// 在1之后
CompletableFuture descFuture = infoFutrue.thenAcceptAsync(res -> {
//4 获取spu介绍
SpuInfoDescEntity spuInfo = spuInfoDescService.getById(res.getSpuId());
skuItemVo.setDesc(spuInfo);
},executor);
// 在1之后
CompletableFuture baseAttrFuture = infoFutrue.thenAcceptAsync(res -> {
//5 获取spu规格参数信息
List attrGroups = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId());
skuItemVo.setGroupAttrs(attrGroups);
}, executor);
// 6.查询当前sku是否参加秒杀优惠
CompletableFuture secKillFuture = CompletableFuture.runAsync(() -> {
R skuSeckillInfo = seckillFeignService.getSkuSeckillInfo(skuId);
if (skuSeckillInfo.getCode() == 0) {
SeckillInfoVo seckillInfoVo = skuSeckillInfo.getData(new TypeReference() {});
skuItemVo.setSeckillInfoVo(seckillInfoVo);
}
}, executor);
// 等待所有任务完成后再返回
CompletableFuture.allOf(imageFuture,saleAttrFuture,descFuture,baseAttrFuture,secKillFuture).get();
return skuItemVo;
}
从那时起,原来的单线程逻辑已经完全优化为多线程执行,完美地提高了效率!
完结撒花o(  ̄▽ ̄ )ブ
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除
itfan123




