什么情况需要自定义线程池
springboot项目中经常会有一些任务需要异步处理,这时候使用@Async
注解十分的方便。但当任务有多种时,有的任务种类执行速度快,有的执行速度慢。有的多久执行完都可以,有的越快越好。当有类似这样的需求时,再把所有的异步任务都放在默认的一个线程池里,就变得不合适了。这时候我们就需要自定义一个或多个线程池,不同的@Async
注解可以根据业务需求,使用自己的线程池。
@Async自定义线程池
一、新建线程池
代码很简单,就是new一个Executor
,然后注册成一个Bean即可。代码如下:
package com.coderbbb.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@EnableAsync
@Configuration
public class AsyncConfig {
@Bean("asyncThread")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(80);
executor.setMaxPoolSize(80);
executor.setQueueCapacity(Integer.MAX_VALUE);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("async-thread-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
}
二、@Async指定线程池
@Async指定线程池,只需要在@Async注解里填写要指定的线程池Bean的名称即可,代码如下:
@Async("asyncThread")
public void add() {
}
关于线程池参数的一些经验
这里介绍线程池的三个关键参数:CorePoolSize
、MaxPoolSize
、QueueCapacity
。
线程池启动的时候,默认先启动CorePoolSize
数量的线程。当任务堆积,并且堆积的任务数量超过QueueCapacity
时,线程池会增加线程数,直到线程数等于MaxPoolSize
的值。那么在实际使用时,应该如何设置这些值呢?可以参考作者的一些经验:
- 如果你的任务耗时比较短(比如没有磁盘IO、网络IO等耗时较多的操作,属于CPU密集型的任务),那么
CorePoolSize
、MaxPoolSize
不需要设置太大,通常设置成CPU核心数的倍数,倍数的最小值当然是1倍,最大值就是CPU已经稳定保持百分之百了,这时候再增大倍数也不会提高速度了,这时候的值就是最大倍数。 - 如果你的任务耗时比较久(有磁盘IO、网络IO等操作,瓶颈在磁盘或网速),那么
CorePoolSize
、MaxPoolSize
可以调大点,直到你的硬件性能达到极限(比如你的程序主要消耗网速,那么极限就是你的网速跑满了。磁盘IO类的也类似,极限就是磁盘读写达到硬件极限了)。 QueueCapacity
这个看你自己的业务需求,如果不想任务高峰时发生溢出报错,可以设置成Interger.MAX_VALUE
。但是设置太大会有个问题,就是线程池会一直以CorePoolSize
运行,不会自动扩容到MaxPoolSize
。