Application.java 初始化创建线程池
@Bean(name = "taskExecutorSub")
public ThreadPoolTaskExecutor taskSchedulerSub() {
ThreadPoolTaskExecutor taskScheduler = new ThreadPoolTaskExecutor();
// 定义一个线程池大小
taskScheduler.setCorePoolSize(5);
taskScheduler.setMaxPoolSize(10);
taskScheduler.setKeepAliveSeconds(1000);
// 线程池名的前缀
taskScheduler.setThreadNamePrefix("taskExecutorSub-");
// 设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保能够被关闭,而不是阻塞住
taskScheduler.setAwaitTerminationSeconds(10 * 60);
taskScheduler.setQueueCapacity(20);
taskScheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
return taskScheduler;
}
使用的时候:
@Autowired
@Qualifier(value = “taskExecutorSub”)
private ThreadPoolTaskExecutor taskExecutorSub;
corePoolSize 核心线程,核心线程会一直存活,当任务进入线程池,只要发现线程数小于核心线程数就会直接创建线程处理任务,所以打印的堆栈日志里面线程数>= 核心线程数
maxPoolSize 需要搭配着队列最大长度queueCapacity 使用,如果不配置队列最大长度,这个参数没有任何意义;因为只有当线程数大于等于核心线程,且任务队列已满才会创建,意思是如果队列没满,那就永远不会创建线程,只能使用核心线程处理
keepAliveSeconds 线程空闲时间, 如果这个线程一直处于空闲时间,则会被回收,销毁仅仅只限于非核心线程,核心线程无法销毁,一旦创建永久存活;
除非设置了allowCoreThreadTimeout=true, 那么核心线程也会退出,直到所有线程数等于0,一般不会设置核心线程数也退出,因为线程的创建和销毁都是需要开销的
threadNamePrefix 线程池的前缀,用来定位问题很有必要,例如出现线程无法释放问题,堆栈日志就可以打印出来具体那个线程池有问题
awaitTerminationSeconds 为了应用需要强制停止的时候能够等线程池的任务执行完再优雅的关机,本人没试验过具体的场景,因为使用kill 命令时直接杀掉进程的,并未看到参数的优势
queueCapacity 设置最大队列长度,如果不设置,极限情况下会OOM,如果任务很多,一直往任务队列里面放,但是线程处理的任务时间长,导致大量的任务都等待再任务队列,最终heap会占满,导致内存溢出
rejectedExecutionHandler 拒绝策略,主要是如果队列满了拒绝任务的时候,是否抛异常等等,一般出现这种情况,说明代码已经有问题了,要不就是线程处理的任务效率有问题,要不就是任务的并发太高,要不就是线程数设置的太小,都需要优化代码来提高处理任务的效率