Spring Boot 配置线程池详解
线程池是多线程的处理机制,线程池一般用于需要大量线程完成任务,并且完成时间较短时使用,大量用于并发框架和异步执行任务。
一、配置线程池的优势
- 降低资源消耗,通过利用已创建的线程降低线程创建和销毁造成的消耗
- 有利于线程的可控性,如果线程无休止创建,会导致内存耗尽。
- 提高系统响应速度,通过使用已存在的线程,不需要等待新线程的创建就可以立即执行当前任务。
二、spring中线程池ThreadPoolTaskExecutor的主要参数说明。
- corePoolSize:核心线程数,默认的核心线程的1,向线程池提交一个任务时,如果线程池已经创建的线程数小于核心线程数,即使此时存在空闲线程,也会通过创建一个新线程来执行新任务,知道创建的线程等于核心线程数时,如果有空闲线程,则使用空闲线程。
- maxPoolSize:最大线程数,默认的最大线程数为Integer.MAX_VALUE 即231-1。当队列满了之后
- keepAliveSeconds:允许线程空闲时间,默认的线程空闲时间为60秒,当线程中的线程数大于核心线程数时,线程的空闲时间如果超过线程的存活时间,则此线程会被销毁,直到线程池中的线程数小于等于核心线程数时。
- queueCapacity:缓冲队列数,默认的缓冲队列数是Integer.MAX_VALUE 即231-1,用于保存执行任务的阻塞队列
- allowCoreThreadTimeOut:销毁机制,allowCoreThreadTimeOut为true则线程池数量最后销毁到0个。allowCoreThreadTimeOut为false销毁机制:超过核心线程数时,而且(超过最大值或者timeout过),就会销毁。默认是false
三、Spring Boot 中创建线程池ThreadPoolTaskExecutor
/**
* @author yly
* @ClassName ThreadPoolConfig
* @Date 2020/2/26 12:10
* @Version 1.0
**/
@Configuration
public class ThreadPoolConfig {
/**
* 核心线程数
* 默认的核心线程数为1
*
*/
private static final int CORE_POOL_SIZE = 5;
/**
* 最大线程数
* 默认的最大线程数是Integer.MAX_VALUE 即2<sup>31</sup>-1
*/
private static final int MAX_POOL_SIZE = 50;
/**
* 缓冲队列数
* 默认的缓冲队列数是Integer.MAX_VALUE 即2<sup>31</sup>-1
*/
private static final int QUEUE_CAPACITY = 100;
/**
* 允许线程空闲时间
* 默认的线程空闲时间为60秒
*/
private static final int KEEP_ALIVE_SECONDS = 30;
/**
* 线程池前缀名
*/
private static final String THREAD_NAME_PREFIX = "Task_Service_Async_";
/**
* allowCoreThreadTimeOut为true则线程池数量最后销毁到0个
* allowCoreThreadTimeOut为false
* 销毁机制:超过核心线程数时,而且(超过最大值或者timeout过),就会销毁。
* 默认是false
*/
private boolean allowCoreThreadTimeOut = false;
@Bean("taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
taskExecutor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);
taskExecutor.setThreadNamePrefix(THREAD_NAME_PREFIX);
taskExecutor.setAllowCoreThreadTimeOut(allowCoreThreadTimeOut);
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//线程池初始化
taskExecutor.initialize();
return taskExecutor;
}
}
其中setRejectedExecutionHandler()设置拒绝执行处理程序。
/**
* Set the RejectedExecutionHandler to use for the ExecutorService.
* Default is the ExecutorService's default abort policy.
* @see java.util.concurrent.ThreadPoolExecutor.AbortPolicy
*/
public void setRejectedExecutionHandler(@Nullable RejectedExecutionHandler rejectedExecutionHandler) {
this.rejectedExecutionHandler =
(rejectedExecutionHandler != null ? rejectedExecutionHandler : new ThreadPoolExecutor.AbortPolicy());
}
ThreadPoolTaskExecutor 的拒绝执行处理程序共有四种,在ThreadPoolExecutor类中
第一种处理程序CallerRunsPolicy
/**
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded.
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
被拒绝任务的处理程序,它直接在{@code execute}方法的调用线程中运行被拒绝的任务,除非执行程序已经关闭,在这种情况下任务被丢弃。
第二种处理程序AbortPolicy
/**
* A handler for rejected tasks that throws a
* {@code RejectedExecutionException}.
*/
public static class AbortPolicy implements RejectedExecutionHandler {
抛出{@code RejectedExecutionException}的被拒绝任务的处理程序。
第三种处理程序DiscardOldestPolicy
/**
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded.
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
被拒绝任务的处理程序,它丢弃最古老的未处理请求,然后重试{@code execute},除非执行程序关闭,在这种情况下任务被丢弃。
第四种处理程序DiscardPolicy
/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
一个处理被拒绝任务的处理程序,它无声地丢弃被拒绝的任务。
四、Spring Boot 中创建线程池ThreadPoolTaskScheduler
/**
* @author yly
* @ClassName ThreadPoolConfig
* @Date 2020/2/26 12:10
* @Version 1.0
**/
@Configuration
public class ThreadPoolConfig {
/**
* 设置调度的执行程序服务的池大小。
*/
private int poolSize = 4;
/**
* 设置任务注册器的调度器
* 在{@link ScheduledThreadPoolExecutor}上设置取消操作模式。默认是{@code false}。
* 如果设置为{@code true},则目标执行程序将切换到“删除-取消”模式(如果可能,则使用软回退)。
* 这个设置可以在运行时修改,例如通过JMX。
*
*/
private boolean removeOnCancelPolicy = true;
/**
* 线程池前缀名
*/
private String threadNamePrefix = "TaskSchedulerThreadPool-";
@Bean("taskScheduler")
public ThreadPoolTaskScheduler taskScheduler(){
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(poolSize);
scheduler.setRemoveOnCancelPolicy(removeOnCancelPolicy);
scheduler.setThreadNamePrefix(threadNamePrefix);
return scheduler;
}
}
ThreadPoolTaskScheduler参数说明
- poolSize :设置调度的执行程序服务的池大小。
- removeOnCancelPolicy : * 设置任务注册器的调度器在{@link ScheduledThreadPoolExecutor}上设置取消操作模式。默认是{@code false}。如果设置为{@code true},则目标执行程序将切换到“删除-取消”模式(如果可能,则使用软回退)。这个设置可以在运行时修改。
- threadNamePrefix :线程池前缀名
其中setErrorHandler()方法是自定义错误处理程序策略
五、 线程池的创建流程
创建线程池时,首先判断线程池的核心线程池是否已满,没满则创建新的工作线程执行任务,否则判断任务队列是否已满,没满则将新提交的任务添加到工作队列,否则判断整个线程池是否已满,没满则创建一个新的工作线程来执行任务,否执行饱和策略。
六、线程池参数推荐设置
- corePoolSize:核心线程数(taskstasktime个线程数),假设系统每秒任务数为100到1000,每个任务耗时0.1秒,则需要1000.1至1000*0.1,即10到100个线程
- maxPoolSize:最大线程数,每秒200个任务需要20个线程,那么当每秒达到1000个任务时,则需要(1000-queueCapacity)*(20/200),即60个线程,可将maxPoolSize设置为60。
- queueCapacity:缓冲队列数,队列长度可以设置为(corePoolSize/tasktime)responsetime: (20/0.1)2=400,即队列长度可设置为400。*
七、使用@Async,异步方法执行
(1)创建SpringBoot web项目
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.yly</groupId>
<artifactId>async</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>async</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
(2)在启动类上添加@EnableAsync和@EnableScheduling(为了查看使用线程池配置定时任务)
/**
* @author 81509
*/
@SpringBootApplication
@EnableAsync
@EnableScheduling
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
}
(3)创建SendSmsService
/**
* @author yly
* @ClassName SendSmsService
* @Date 2020/2/26 11:31
* @Version 1.0
**/
@Service
@Async("taskExecutor")//可以加在类上或方法上
public class SendSmsService {
Logger logger = LoggerFactory.getLogger(getClass());
public void sendMerchant() {
logger.info("通知商铺--------开始");
try {
logger.info("sleep----3秒");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("通知商铺--------完成");
}
public void sendConsumer() {
logger.info("通知消费者---------开始");
try {
logger.info("sleep----1秒");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("通知消费者---------完成");
}
}
(4)配置线程池
/**
* @author yly
* @ClassName ThreadPoolConfig
* @Date 2020/2/26 12:10
* @Version 1.0
**/
@Configuration
public class ThreadPoolConfig {
/**
* 核心线程数
* 默认的核心线程数为1
*
*/
private static final int CORE_POOL_SIZE = 5;
/**
* 最大线程数
* 默认的最大线程数是Integer.MAX_VALUE 即2<sup>31</sup>-1
*/
private static final int MAX_POOL_SIZE = 50;
/**
* 缓冲队列数
* 默认的缓冲队列数是Integer.MAX_VALUE 即2<sup>31</sup>-1
*/
private static final int QUEUE_CAPACITY = 100;
/**
* 允许线程空闲时间
* 默认的线程空闲时间为60秒
*/
private static final int KEEP_ALIVE_SECONDS = 30;
/**
* 线程池前缀名
*/
private static final String THREAD_NAME_PREFIX = "Task_Service_Async_";
/**
* allowCoreThreadTimeOut为true则线程池数量最后销毁到0个
* allowCoreThreadTimeOut为false
* 销毁机制:超过核心线程数时,而且(超过最大值或者timeout过),就会销毁。
* 默认是false
*/
private boolean allowCoreThreadTimeOut = false;
@Bean("taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
taskExecutor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);
taskExecutor.setThreadNamePrefix(THREAD_NAME_PREFIX);
taskExecutor.setAllowCoreThreadTimeOut(allowCoreThreadTimeOut);
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
taskExecutor.initialize();
return taskExecutor;
}
}
(5)创建demo类调用异步方法,需要加上@Component加入到容器中
/**
* @author yly
* @ClassName demo
* @Date 2020/2/26 11:37
* @Version 1.0
**/
@Component
public class demo {
@Autowired
private SendSmsService sendSmsService;
@Scheduled(fixedRate = 1000)//定时任务,一秒执行一次
public void test() {
sendSmsService.sendMerchant();
sendSmsService.sendConsumer();
}
}
(6)执行结果