前言
假设写购物网站的时候,后台项目需要去查询退款商品 是否有使用优惠卷 并且需要查询原价和成本价 等,我们后端必须去将这个售后商品各个多元化维度信息全部展示给用户(专业名称应该叫sku和spu),如果我们采用单线程的方式去做,按部就班的串行查询商品的信息,ServiceImpl层查询代码如下:
@Override //SkuInfoServiceImpl
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的图片信息
List<SkuImagesEntity> skuImagesEntities = skuimagesService.list(new QueryWrapper<SkuimagesEntity>().eq("sku_id", skuId));
skuItemVo.setimages(skuimagesEntities);
//3、获取spu的销售属性组合-> 需要依赖第1步的请求返回值中的spuId
List<SkuItemSaleAttrVo> 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<SpuItemAttrGroupVo> spuItemAttrGroupVos=productAttrValueService.getProductGroupAttrsBySpuId(spuId, catalogId);
skuItemVo.setGroupAttrs(spuItemAttrGroupVos);
// 6、秒杀商品的优惠信息 查看支付流水表信息 是否有使用优惠卷
PayStatItem vo=payFegin.selectByPayCode(skuInfoEntity.getPayCode());
skuItemVo.payCoupon(vo.getPayCoupon());
return skuItemVo;
}
这样按部就班查询没问题,但是速度必然会慢,那怎么解决这个问题呢? 使用异步处理?还是?
先来看一下 我用apiFox 测试 结果如下
请看优化步骤
1.CompletableFuture类介绍 在Java 8中, 新增加了一个包含50个方法左右的类: CompletableFuture,提供了非常强大的异步编程的能力。它通过函数式编程,以回调的方式处理计算结果,并且提供了转换和组合等方法。
CompletableFuture 提供了四个静态方法来创建一个异步操作。
static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
runAsync方法不支持返回值。
supplyAsync可以支持
————————————————
2.请看我在项目当中是这样去使用的
先自己初始化一个新的线程池 请看代码
package com.itzhouwei.commerce.thread;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* <h1>多线程线程池配置 为了后面多线程异步编排处理<h1/>
* @description:
* @author:zw,微信:yingshengzw
* @date: 2023/2/6
* @Copyright: 公众号:搬砖暗夜码农 | 博客:https://itzhouwei.com - 沉淀、分享、成长,让自己和他人都能有所收获!
*/
@Component
public class MyThreadPool {
/**
* int corePoolSize: 核心线程数量,线程池一旦创建好,就有了固定的线程数,
* 这些线程(数)用于接收并且处理异步任务;
* 如果任务完结,那么这些线程依然还会存在,处于空闲状态,
* 除非设置allowCoreThreadTimeOut
* int maximumPoolSize: 线程池中允许的最大线程数量,如果请求任务并发超过corePoolSize,
* 那么,最大的线程数也不会超过maximumPoolSize(弹性伸缩)
* long keepAliveTime: 超时时间,当超过corePoolSize的部分线程数超时了,则释放
* TimeUnit unit: 超时的时间单位
* BlockingQueue<Runnable> workQueue: 阻塞队列,任务执行之前,
* 超出的任务会先保存到一个队列中,
* 线程一旦空闲,则从队列中获得新的任务开始执行
* ThreadFactory threadFactory: 线程工厂,怎么创建线程的
* RejectedExecutionHandler handler: 驳回策略,队列满了,或者队列达到性能的瓶颈,
* 使用子的策略方式来处理任务的驳回
* AbortPolicy - : 新任务进来,则直接丢弃(抛异常)
* CallerRunsPolicy - : 不用线程,直接调用run方法,为了保证任务一个都不丢失,
* 但是会出现数据处理挤压的情况
* DiscardOldestPolicy - : 丢弃老的任务,使用新的任务
* DiscardPolicy - : 丢弃新的任务(不会抛出异常)
*
*/
@Bean
public ThreadPoolExecutor threadPoolExecutor() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
3,
10,
30,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(5000),
Executors.defaultThreadFactory(), // 默认的线程工厂
new ThreadPoolExecutor.AbortPolicy()
);
return executor;
}
}
第二步 由于我自己对CompletableFuture 进行封装一点 简化一下代码
package com.itzhouwei.commerce.thread;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Supplier;
/**
* <h1>异步串行话 对象<h1/>
* @description:
* @author:zw,微信:yingshengzw
* @date: 2023/2/6
* @Copyright: 公众号:搬砖暗夜码农 | 博客:https://itzhouwei.com - 沉淀、分享、成长,让自己和他人都能有所收获!
* @param <T> 返回值类型 可以是VO 也可以是String
*/
@Component
public class CompletableFutureX<T>{
@Autowired
private ThreadPoolExecutor threadPoolExecutor;
/***
* 异步对象并且带有返回结果集的操作方法
* @param supplier 可以传入一个匿名类 进行操作 比如 ()->{}
* @param <T> 返回值类型 可以是VO 也可以是String
* @return
*/
public <T> CompletableFuture<T> supplyAsync(Supplier<T> supplier) {
return CompletableFuture.supplyAsync(supplier,threadPoolExecutor);
}
/***
* 多任务组合 并且 等待所有任务完成 返回结果
* @param cfs 串行 线程 返回值
* @return
*/
public CompletableFuture<Void> allOfAndGet(CompletableFuture<?>... cfs) {
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.allOf(cfs);
try {
voidCompletableFuture.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return voidCompletableFuture;
}
}
第三步 改造代码如下
@Autowired
private CompletableFutureX completableFutureX;
@Override //SkuInfoServiceImpl
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的图片信息
CompletableFuture skuId= completableFutureX.supplyAsync(
List<SkuImagesEntity> images= skuimagesService.list(
new QueryWrapper<SkuimagesEntity>().eq("sku_id", skuId));
skuItemVo.setimages(skuimagesEntities);
return images; );
//3、获取spu的销售属性组合-> 需要依赖第1步的请求返回值中的spuId
CompletableFuture saleAttrVosFuture= completableFutureX.supplyAsync(
List<SkuItemSaleAttrVo> saleAttrVos=skuSaleAttrValueService.listSaleAttrs(spuId);
skuItemVo.setSaleAttr(saleAttrVos);
return saleAttrVos;
);
//4、获取spu的介绍-> 需要依赖第1步的请求返回值中的spuId
CompletableFuture spuInfoDescEntityFuture= completableFutureX.supplyAsync(
SpuInfoDescEntity byId = spuInfoDescService.getById(spuId);
skuItemVo.setDesc(byId);
return byId;
);
//5、获取spu的规格参数信息-> 需要依赖第1步的请求返回值中的spuId和catalogId
CompletableFuture spuItemAttrGroupVoFuture= completableFutureX.supplyAsync(
List<SpuItemAttrGroupVo> spuItemAttrGroupVos =
productAttrValueService.getProductGroupAttrsBySpuId(spuId, catalogId);
skuItemVo.setGroupAttrs(spuItemAttrGroupVos);
return spuItemAttrGroupVos;);
// 6、秒杀商品的优惠信息 查看支付流水表信息 是否有使用优惠卷
CompletableFuture payStatItemFuture= completableFutureX.supplyAsync(
PayStatItem vo=payFegin.selectByPayCode(skuInfoEntity.getPayCode());
skuItemVo.payCoupon(vo.getPayCoupon());
return vo;);
completableFutureX.allOfAndGet(skuId,saleAttrVosFuture,spuInfoDescEntityFuture,spuItemAttrGroupVoFuture,payStatItemFuture);
return skuItemVo;
}
在用apiFox 测试如下
总结
不要说你在项目当中没有用到多线程 只是你并不想去改变而已 还没用中间件 就提升接口一点效率 其实生命的意义在折腾