线程池即一个管理若干个可执行线程的容器。
一个线程的生命周期(创建,就绪,运行,阻塞,销毁)创建和销毁的资源开销是很大的,线程池可以减少创建和销毁线程次数,使工作线程有效的重复利用。
系统中创建太多的线程也会导致线程过度切换,消耗过度系统资源导致任务处理会变得更慢,线程池可以控制容器中的线程数量,结合阻塞等待和拒绝策略保护系统资源。
工作任务在创建和销毁线程上花费的时间和系统资源要比花费在处理实际的业务处理的时间和资源更多时。
对创建线程资源有严格把控时(无限制创建会导致内存泄漏)(使用线程池也有内存泄漏风险)。
多核处理器分治处理任务(单核处理器不适合对CPU密集型任务使用线程池,对IO/CUP混合型任务会有性能提升)
以下六种JDK创建线程池方式不建议使用(创建的线程池用的是无界任务队列)
Executors.newCachedThreadPool 创建一个可缓存线程池
Executors.newFixedThreadPool 创建一个定长线程池
Executors.newScheduledThreadPool 创建一个定长线程池
Executors.newSingleThreadExecutor 创建一个单线程化的线程池
Executors.newSingleThreadScheduledExecutor 创建一个单线程可执行周期性任务的线程池
Executors.newWorkStealingPool 创建一个任务可窃取线程池
创建线程池五个重要的参数
corePoolSize:核心线程数
maxPoolSize:最大线程数
keepAliveTime:线程空闲时间
rejectedExecutionHandler:任务拒绝处理器
workQueue:任务队列
线程会优先使用核心线程,当核心线程满了任务线程会进入任务队列,如果任务队列是无界队列,最大线程数参数则无意义,此时也可能会出现内存溢出,最大值为Integer大小。如果任务是有界队列,任务线程会进入队列等待,若此队列满了会新建线程,新建线程最大为(最大线程数减去核心线程数),若核心线程满了则会进行拒绝策略。
拒绝策略
AbortPolicy 丢弃并抛出异常
CallerRunsPolicy 当前线程执行任务
DiscardPolicy 丢弃
DiscardOldestPolicy 从队列中踢出最先进入队列的任务
任务队列
ArrayBlockingQueue:基于数组的有界阻塞队列,FIFO先进先出排序
LinkedBlockingQueue:基于链表的阻塞队列,FIFO排序,由于增删频繁效率会比ArrayBlockingQueue高
SynchronousQueue:不存储元素,每次插入必须等到另一个线程调用移除操作,否则阻塞(Executors.newCachedThreadPool 会用到此队列,核心线程设置为0,最大线程设置Integer.Max_VALUE,线程时间设置60s,用来实现缓存队列)
PriorityBlockingQueue:一个具有优先级的无限阻塞队列
@Bean(name = "myThreadPool")
public ThreadPoolTaskExecutor threadPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(8);
// 设置最大线程数
executor.setMaxPoolSize(16);
// 设置队列容量
executor.setQueueCapacity(1024);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(60);
// 设置拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 设置默认线程名称
executor.setThreadNamePrefix("myThreadPool---");
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
// 初始化线程池
executor.initialize();
return executor;
}