文章目录
线程回顾
参见link
通过如下两种方式初始化线程池
Executors.newFixedThreadPool(3);
//或者
new ThreadPollExecutor(corePoolSize,maximumPoolSize,keepAliveTime,TimeUnit,unit,workQueue,threadFactory,handler);
通过线程池性能稳定,也可以获取执行结果,并捕获异常,但是,在业务复杂情况下,一个异步调用可能会依赖另一个异步调用的执行结果
线程池的 7 大参数
1、线程池创建,准备好 core 数量 的核心线程,准备接受任务
2、新的任务进来,用 core 准备好的空闲线程执行
core 满了,就将再进来的任务放入阻塞队列中,空闲的 core 就会自己去阻塞队列获取任务执行
阻塞队列也满了,就直接开新线程去执行,最大只能开到 max 指定的数量
max 都执行好了,Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁,终保持到 core 大小
如果线程数开到了 max 数量,还有新的任务进来,就会使用 reject 指定的拒绝策略进行处理
3、所有的线程创建都是由指定的 factory 创建的
面试;
一个线程池 core 7、max 20 ,queue 50 100 并发进来怎么分配的 ?
先有 7 个能直接得到运行,接下来 50 个进入队列排队,再多开 13 个继续执行,线程70个被安排上了,剩下30个默认拒绝策略
常见的 4 种线程池
newCacheThreadPool 创建一个可缓存的线程池,如果线程池长度超过需要,可灵活回收空闲线程,若无可回收,则新建线程
newFixedThreadPool 创建一个指定长度的线程池,可控制线程最大并发数,超出的线程会再队列中等待
newScheduleThreadPool 创建一个定长线程池,支持定时及周期性任务执行
newSingleThreadExecutor 创建一个单线程化的线程池,她只会用唯一的工作线程来执行任务,保证所有任务
开发中为什么使用线程池
降低资源的消耗
通过重复利用已创建好的线程降低线程的创建和销毁带来的损耗
提高响应速度
因为线程池中的线程没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
提高线程的客观理性
线程池会根据当前系统的特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销,无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配
CompletableFuture 异步编排
创建异步对象
CompletableFuture 提供了四个静态方法来创建一个异步操作
1、runXxx 都是没有返回结果的,supplyXxxx都是可以获取返回结果的
2、可以传入自定义的线程池,否则就是用默认的线程池
3、根据方法的返回类型来判断是否该方法是否有返回类型
计算完成时回调方法
whenComplete 可以处理正常和异常的计算结果,exceptionally 处理异常情况
whenComplete 和 whenCompleteAsync 的区别
whenComplete :是执行当前任务的线程继续执行 whencomplete 的任务
whenCompleteAsync: 是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行
方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)
handle 方法
和 complete 一样,可以对结果做最后的处理(可处理异常),可改变返回值
线程串行方法
thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任物的返回值
thenAccept方法:消费处理结果,接受任务处理结果,并消费处理,无返回结果
thenRun 方法:只要上面任务执行完成,就开始执行 thenRun ,只是处理完任务后,执行 thenRun的后续操作
带有 Async 默认是异步执行的,同之前,
两任务组合 - 都要完成
两个任务必须都完成,触发该任务
thenCombine: 组合两个 future,获取两个 future的返回结果,并返回当前任务的返回值
thenAccpetBoth: 组合两个 future,获取两个 future 任务的返回结果,然后处理任务,没有返回值
runAfterBoth:组合 两个 future,不需要获取 future 的结果,只需要两个 future处理完成任务后,处理该任务,
两任务组合 - 一个完成
当两个任务中,任意一个future 任务完成时,执行任务
applyToEither;两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值
acceptEither: 两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值
runAfterEither:两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返回值
多任务组合
allOf:等待所有任务完成
anyOf:只要有一个任务完成
商品详情获取
发生在item.html
com/atguigu/gulimall/product/service/impl/SkuInfoServiceImpl.java
@Autowired
ThreadPoolExecutor executor; //线程池
@Override
public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {
SkuItemVo skuItemVo = new SkuItemVo();
CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
// 1、sku基本获取 pms_sku_info
// 根据 skuid 查询出 spuInfo对象
SkuInfoEntity info = getById(skuId);
skuItemVo.setSkuInfo(info);
return info;
}, executor);
CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {
Long spuId = res.getSpuId();
// 3、获取spu的销售属性组合
List<SkuItemSalAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrBySpuId(spuId);
skuItemVo.setSaleAttr(saleAttrVos);
}, executor);
CompletableFuture<Void> spuDescFuture = infoFuture.thenAcceptAsync((res) -> {
Long spuId = res.getSpuId();
// 4、获取spu的介绍
// 通过spuid查询出spu描述信息
SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(spuId);
skuItemVo.setDesc(spuInfoDescEntity);
}, executor);
CompletableFuture<Void> spuAttrFuture = infoFuture.thenAcceptAsync(res -> {
// 5、获取spu的规格参数信息
List<SpuItemAttrGroupVo> spuItemAttrGroupVoList = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId());
skuItemVo.setGroupAttrs(spuItemAttrGroupVoList);
}, executor);
CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
// 2、sku的图片信息 pms_sku_images
List<SkuImagesEntity> images = imagesService.getImagesBySkuId(skuId);
skuItemVo.setImages(images);
}, executor);
//3.查询当前商品是否参与秒杀
CompletableFuture<Void> seckillFuture = CompletableFuture.runAsync(() -> {
R r = seckillFeignService.getSkuSeckillInfo(skuId);
if (r.getCode() == 0) {
SeckillInfoVo seckillInfoVo = r.getData("data", new TypeReference<SeckillInfoVo>() {
});
skuItemVo.setSeckillInfoVo(seckillInfoVo);
}
}, executor);
//等到所有任务都完成
CompletableFuture.allOf(imageFuture, saleAttrFuture, spuAttrFuture, spuDescFuture,seckillFuture).get();
return skuItemVo;
}
自定义线程池配置类
com/atguigu/gulimall/product/config/MyThreadConfig.java
@Configuration
public class MyThreadConfig {
@Bean
public ThreadPoolExecutor genThreadPool(ThreadPoolConfigProperties pool) { //由于ThreadPoolConfigProperties pool已经放入IOC,所以这儿会自动注入参数
return new ThreadPoolExecutor(pool.getCoreSize(), pool.getMaxSize(), pool.getKeepLiveTime(),
TimeUnit.SECONDS, new LinkedBlockingDeque<>(100000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
}
}
看看ThreadPoolConfigProperties这个是啥,结果又卖了一个官司
@ConfigurationProperties(prefix = "gulimall.thread")
@Component
@Data
public class ThreadPoolConfigProperties {
private Integer coreSize;
private Integer maxSize;
private Integer keepLiveTime;
}
去配置文件application.properties看看
gulimall.thread.core-size=20
gulimall.thread.max-size=200
gulimall.thread.keep-live-time=10
是不是和prefix = "gulimall.thread"很一致,为了在配置文件中键入时有提示,导入依赖
<!--在application.properties中配置自定义配置类时,提示需要配置的内容-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>