一、配置文件application.yml
# 线程池配置
thread:
pool:
executor:
config:
core-pool-size: 20
max-pool-size: 50
keep-alive-time: 5000
block-queue-size: 5000
policy: CallerRunsPolicy
二、线程池配置属性类
@Data
// 读取配置文件thread.pool.executor.config下的属性
// 忽略无法映射的属性
@ConfigurationProperties(prefix = "thread.pool.executor.config", ignoreInvalidFields = true)
public class ThreadPoolConfigProperties {
/** 核心线程数 */
private Integer corePoolSize = 20;
/** 最大线程数 */
private Integer maxPoolSize = 200;
/** 最大等待时间 */
private Long keepAliveTime = 10L;
/** 最大队列数 */
private Integer blockQueueSize = 5000;
/*
* AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
* DiscardPolicy:直接丢弃任务,但是不会抛出异常
* DiscardOldestPolicy:将最早进入队列的任务删除,之后再尝试加入队列的任务被拒绝
* CallerRunsPolicy:如果任务添加线程池失败,那么主线程自己执行该任务
* */
private String policy = "AbortPolicy";
}
三、线程池配置类
@Slf4j
// 开启spring异步方法的支持,使@Async注解的方法运行在异步模式下
@EnableAsync
// 表示这个类是一个Spring配置类,用于定义配置信息和Bean
@Configuration
// 启动对ThreadPoolConfigProperties类的配置属性的支持
@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
public class ThreadPoolConfig {
// 标记该方法是bean工厂方法,返回的对象交给spring容器管理
@Bean
// 条件创造bean:容器中没有ThreadPoolExecutor类型的bean时才进行创建
@ConditionalOnMissingBean(ThreadPoolExecutor.class)
public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties properties) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
// 实例化策略
RejectedExecutionHandler handler;
switch (properties.getPolicy()){
case "AbortPolicy":
handler = new ThreadPoolExecutor.AbortPolicy();
break;
case "DiscardPolicy":
handler = new ThreadPoolExecutor.DiscardPolicy();
break;
case "DiscardOldestPolicy":
handler = new ThreadPoolExecutor.DiscardOldestPolicy();
break;
case "CallerRunsPolicy":
handler = new ThreadPoolExecutor.CallerRunsPolicy();
break;
default:
handler = new ThreadPoolExecutor.AbortPolicy();
break;
}
// 创建线程池
return new ThreadPoolExecutor(properties.getCorePoolSize(),
properties.getMaxPoolSize(),
properties.getKeepAliveTime(),
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(properties.getBlockQueueSize()),
Executors.defaultThreadFactory(),
handler);
}
}
四、线程池知识点回顾
1. ThreadPoolExecutor参数
下面是ThreadPoolExecutor类的一个构造方法:
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
根据如上构造方法,在new一个ThreadPoolExecutor时需要传入如下参数
return new ThreadPoolExecutor(properties.getCorePoolSize(),
properties.getMaxPoolSize(),
properties.getKeepAliveTime(),
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(properties.getBlockQueueSize()),
Executors.defaultThreadFactory(),
handler);
(1)核心线程数:corePoolSize
任务队列未达到队列容量时,最大可以同时运行的线程数量为corePoolSize。
(2)最大线程数:maxPoolSize
任务队列已满,此时可同时运行的线程数量变为maxPoolSize。
(3)等待时间keepAliveTime
线程池中的线程数量大于corePoolSize,如果没有新任务提交,核心线程外的线程不会立刻销毁,会等待keepAliveTime再被回收销毁。
(4)参数的时间单位:unit
一般是TimeUnit.SECOND
(5)阻塞队列:workQueue
(6)拒绝策略:handler
2. 线程池处理任务流程
结合上述参数,线程池处理任务的流程可以这样描述。
- 提交任务,判断核心线程池是否已满,如果没有满(当前线程数 < corePoolSize),则创建线程
- 如果核心线程池已满,但没达到最大线程数(corePoolSize <= 当前线程数 < maxPoolSize),则把该任务放到阻塞队列里等待执行
- 如果阻塞队列已满,但当前线程数 < maxPoolSize,则新建一个线程来执行任务。
- 如果当前线程数 >= maxPoolSize,那么会根据拒绝测觉拒绝提交的任务。