SpringBoot 使用线程池
/**
* 线程池的配置和初始化
*/
@Data
@ConfigurationProperties("executor")
@Configuration
public class ThreadExecutorPool {
private Integer corePoolSize;
private Integer maxPoolSize;
private Integer queueCapacity;
private Integer keepAliveSeconds;
private String threadNamePrefix;
@Bean("taskAsyncPool")
public ThreadPoolTaskExecutor taskAsyncPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setThreadNamePrefix(getThreadNamePrefix());
executor.initialize();
return executor;
}
}
配置文件
executor:
corePoolSize: 5
maxPoolSize: 10
queueCapacity: 20
keepAliveSeconds: 60
threadNamePrefix: XCExecutor-
- 在SpringBoot启动类上增加
@EnableAsync
注解 - 然后再需要多线程执行的方法上增加
@Async(value = "taskAsyncPool")
注解即可
线程池
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后再创建线程后自动启动这些任务。
为什么使用线程池
一般使用线程的方式有两种:一种是继承Thread
类,一种是实现Runnable
的接口,Thread
类也是实现了Runnable
接口。这两种方式在运行接收后都会被虚拟机销毁。如果线程数量多的话,频繁的创建和销毁就会大大的浪费时间和效率,而且会浪费内存。为了减少创建和销毁线程的次数,让每个线程可以多次使用,可根据系统情况调整执行的线程数量,防止消耗过多内存和是时间。
一个线程池包括以下四个基本部分:
- 线程池管理器(
ThreadPool
) : 用于创建并管理线程池,包括销毁线程池和添加新任务- 工作线程(
PoolWorker
) : 线程池中线程,在没有任务时处于等待装态,主要循环执行的任务- 任务接口(
Task
) : 每个任务都必须实现的接口,以供工作线程调度任务的执行,主要规定了任务的入口,任务执行完后的收尾工作,以及任务的执行装态等- 任务队列(
taskQueue
) : 用于存放没有处理的任务。一种缓冲机制。
特点
- 线程复用
- 控制最大并发数量
- 管理线程
优点
- 降低了资源消耗。通过复用机制降低了线程的创建和销毁带来的消耗
- 提高响应速度。当任务来临时,不需要等待创建线程的时间就能立即执行
- 提高线程的管理。如果无限制的创建,不仅消耗系统资源,还会降低系统的稳定性,使用线程池可以统一分配,调优和控制
线程池配置说明
线程池的最上层接口是Executor
,这个接口定义了一个核心方法execute(Runnable command)
,这个方法最后被ThreadPoolExecutor
类实现,用来传入任务。而且ThreadPoolExecutor
是线程池的核心类,此类构造方法如下:
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;
}
corePoolSize
:核心线程池的大小,如果核心线程池有空闲位置,这是新的任务就会被核心线程池新建一个线程执行,执行完毕后不会销毁线程,线程会进入缓存队列等待再次被运行。maximumPoolSize
:线程池能创建最大的线程数量。如果核心线程池和缓存队列都已经满了,新的任务进来就会创建新的线程来执行。但是数量不能超过maximunPoolSize
,否侧会采取拒绝接受任务策略,我们下面会具体分析。keepAliveTime
:非核心线程能够空闲的最长时间,超过时间,线程终止。这个参数默认只有在线程数量超过核心线程池大小时才会起作用。只要线程数量不超过核心线程大小,就不会起作用。unit
:时间单位,和keepAliveTime
配合使用。workQueue
:缓存队列,用来存放等待被执行的任务。threadFactory
:线程工厂,用来创建线程,一般有三种选择策略。
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue
handler
:拒绝处理策略,线程数量大于最大线程数就会采用拒绝处理策略
ThreadPoolExecutor.AbortPolicy
:丢弃任务并抛出RejectedExecutionException
异常。ThreadPoolExecutor.DiscardPolicy
:也是丢弃任务,但是不抛出异常。ThreadPoolExecutor.DiscardOldestPolicy
:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)ThreadPoolExecutor.CallerRunsPolicy
:由调用线程处理该任务- 实现
RejectedExecutionHandler
接口,也可自定义处理器。