线程池是什么相信大家都是知道的,所以这里就不做解释了,直接看相关知识点吧。
初始化线程池方法
继承ThreadPool
实现Runnable
实现Callable 接口+ FutureTask (可以拿到返回结果,可以处理异常)
核心参数
- corePoolSize:核心线程数。(理解为某个公司的长期员工)
- maximumPoolSize:最大线程数。(理解为当前公司项目太多,需要招募短期员工,这个时候长期员工+短期员工数量就是最大线程数)
- 这里最大线程数需要大于核心线程数,不然会报错:
- keepAliveTime:空闲线程存活时间。
- TimeUnit:时间单位。
- BlockingQueue:线程池任务队列。
- ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
- SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们。
- PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
- DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素。
- LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似,还含有非阻塞方法。
- LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
- ThreadFactory:创建线程的工厂。
- RejectedExecutionHandler:拒绝策略。
任务队列等于阻塞队列?
-
任务队列(Task Queue):
- 任务队列是线程池中用于存放待执行任务的队列。
- 当一个任务被提交到线程池时,如果线程池中的工作线程数已达到最大值,或者所有工作线程都在忙碌,那么这个任务就会被放入任务队列中等待后续执行。
- 任务队列可以是无界的(如
LinkedBlockingQueue
),也可以是有界的(如ArrayBlockingQueue
),具体取决于线程池的配置。
-
阻塞队列(Blocking Queue):
- 阻塞队列是一种特殊的队列,它支持线程安全的入队(put)和出队(take)操作。
- 当队列为空时,从队列中取元素的操作(如
take
)会阻塞,直到队列中有元素可用。 - 当队列已满时,向队列中添加元素的操作(如
put
)会阻塞,直到队列中有空间可用。 - 阻塞队列是实现任务队列的一种常见方式,但不是唯一的方式。例如,
SynchronousQueue
也是一种任务队列,但它不存储元素,而是直接将元素从一个生产者线程传递给消费者线程。
因此,任务队列是线程池中用于管理待执行任务的队列,而阻塞队列是一种支持线程安全操作的队列。阻塞队列可以作为实现任务队列的一种方式,但任务队列的概念更广泛,不一定局限于阻塞队列。在线程池的上下文中,任务队列通常是指用于存放待执行任务的队列,而这个队列可以是基于阻塞队列实现的。
拒绝策略
- AbortPolicy(默认策略) :直接抛出
RejectedExecutionException
异常,阻止系统正常运行。 - **CallerRunsPolicy:**主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度。
- DiscardPolicy:直接丢弃无法处理的任务,不抛出异常,也不执行任务。
- DiscardOldestPolicy:丢弃最旧的任务请求,然后尝试再次提交当前任务。
线程池的运行流程
- 线程池创建,准备好core 数量的核心线程,准备接受任务
- 新的任务进来,用core 准备好的空闲线程执行。 (1) 、core 满了,就将再进来的任务放入阻塞队列中。空闲的core 就会自己去阻塞队 列获取任务执行 (2) 、阻塞队列满了,就直接开新线程执行,最大只能开到max 指定的数量 (3) 、max 都执行好了。Max-core 数量空闲的线程会在keepAliveTime 指定的时间后自 动销毁。最终保持到core 大小 (4) 、如果线程数开到了max 的数量,还有新任务进来,就会使用reject 指定的拒绝策 略进行处理
- 所有的线程创建都是由指定的factory 创建的。
一个线程池core 7; max 20 ,queue:50,100 并发进来怎么分配的; 先有7 个能直接得到执行,接下来50 个进入队列排队,在多开13 个继续执行。现在70 个 被安排上了。剩下30 个默认拒绝策略。拒绝策略一般是抛弃,如果不想抛弃还要执行,可以使用同步的方式执行,或者丢弃最老的。
四种常见的线程池
- newCachedThreadPool创建一个可缓存线程池 ,如果线程池长度超过处理需要,可灵活回收空闲线程,若 无可回收,则新建线程。核心线程固定是0,所有都可回收
- newFixedThreadPool创建一个固定长线程池,可控制线程最大并发数, 超出的线程会在队列中等待。固定大小,核心 = 最大
- newScheduledThreadPool创建一个固定长线程池,支持定时及周期性任务执行。 定时任务线程池
- newSingleThreadExecutor创建一个 单线程化的线程池 ,它只会用唯一的工作线程来执行任务,保证所有任务 按照指定顺序(FIFO, LIFO, 优先级)执行。后台从队列里面获取任务 挨个执行
SpringBoot整合线程池
1、添加配置类,新建线程池
java
package cn.cloud.xmall.product.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Configuration
public class MyThreadConfig {
@Bean
public ThreadPoolExecutor threadPoolExecutor(){
return new ThreadPoolExecutor(
10,
100,
20,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
};
}
2.想要在配置文件中手动的配置参数 新建一个配置属性类
java
package cn.cloud.xmall.product.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
@ConfigurationProperties(prefix = "mall.thread")
@Component //加入容器
@Data
public class ThreadPollConfigProperties {
private Integer coreSize;
private Integer maxSize;
private Integer keepAliveTime;
}
可以在依赖种添加此依赖,在配置文件中就会有我们自己配置属性的提示
java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
3.配置文件配置属性
java
#线程池配置
mall:
thread:
core-size: 10
max-size: 100
keep-alive-time: 20
4.使用配置文件中的属性
@
EnableConfigurationProperties(ThreadPollConfigProperties.class),如果配置文件类没有添加@Component加入容器可以使用这种方式
java
package cn.cloud.xmall.product.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
//@EnableConfigurationProperties(ThreadPollConfigProperties.class)
@Configuration
public class MyThreadConfig {
@Bean
public ThreadPoolExecutor threadPoolExecutor(ThreadPollConfigProperties pool){
return new ThreadPoolExecutor(
pool.getCoreSize(),
pool.getMaxSize(),
pool.getKeepAliveTime(),
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
};
}
5.注入线程池
@Autowired
private ThreadPoolExecutor executor;
6.异步编排
java
@Override
public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {
SkuItemVo skuItemVo = new SkuItemVo();
//1.使用自己的线程池来新建异步任务
CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
//1.查询基本信息 pms_sku_info
SkuInfoEntity info = getById(skuId);
skuItemVo.setInfo(info);
return info;
}, executor);
//2.根据一号任务来继续调用
CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {
//3.获取当前spu的销售属性组合
List<SkuItemSaleAttrVo> saleAttrVos = saleAttrValueService.getSaleAttrsBySpuId(res.getSpuId());
skuItemVo.setSaleAttr(saleAttrVos);
}, executor);
//3.根据一号任务来继续调用
CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync((res) -> {
//4.获取Spu的介绍 pms_spu_info_desc
SpuInfoDescEntity spuInfo = spuInfoDescService.getById(res.getSpuId());
skuItemVo.setDesc(spuInfo);
}, executor);
//4.根据一号任务来继续调用
CompletableFuture<Void> baseAttrFuture = infoFuture.thenAcceptAsync((res) -> {
//5.获取spu的规格参数信息
List<SpuItemAttrGroupVo> attrGroups = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId());
skuItemVo.setGroupAttrs(attrGroups);
}, executor);
//此任务不需要根据一号任务的返回调用,所以开一个新线程
CompletableFuture<Void> imagesFuture = CompletableFuture.runAsync(() -> {
//2.获取sku的图片信息 pms_sku_images
List<SkuImagesEntity> images = imagesService.getImagesBySkuId(skuId);
skuItemVo.setImages(images);
}, executor);
//等待所有任务都完成
//TODO 可以选择有异常情况下的处理结果
CompletableFuture.allOf(saleAttrFuture,descFuture,baseAttrFuture,imagesFuture).get();
return skuItemVo;
}
该文档后续会继续更新,希望读者可以在评论区留言我可以根据具体的问题进行更新~