本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
今天说一下线程池的使用和一些参数的配置
在jdk中有一个Executors框架提供了各种类型的线程池
- public static ExecutorService newCachedThreadPool() 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。
- public static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程,超出的线程会在队列中等待。
- public static ExecutorService newSingleThreadExecutor() 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
- public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建一个定长线程池,支持定时及周期性任务执行。
然而阿里开发手册上并不推荐使用jdk自带的线程池框架来创建线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
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;
}
经过查看源码,Executors框架在创建线程池时使用的是LinkedBlockingQueue()。也就是说通过 new LinkedBlockingQueue()创建的队列长度是Interger.MAX_VALUE。任务队列的长度并没有限制,导致任务大量排队不执行拒绝策略,导致队列中任务堆积过多导致OOM。
那么这时候我们就需要手动创建线程池
手动创建线程池有什么好处呢,使我们更加明确线程池的运行规则,更加灵活可控制线程池参数,规避资源耗尽的风险。 下面我们看一下如何创建一个优秀的线程池
@Bean
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
// 核心线程数
threadPoolTaskExecutor.setCorePoolSize(corePoolSize);
// 最大线程数
threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize);
// 工作队列容量
threadPoolTaskExecutor.setQueueCapacity(queueCapacity);
// 拒绝策略:任务添加失败由调用线程执行
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 线程的名字前缀
threadPoolTaskExecutor.setThreadNamePrefix("thread-order-");
return threadPoolTaskExecutor;
}
线程池的七个核心参数
- corePoolSize:核心线程数
- maxPoolSize:最大线程数
- queueCapacity:工作队列容量
- keepAliveTime:空闲线程存活时间
- unit:空闲线程存活时间单位
- workQueue:工作队列
- handler:拒绝策略
在实际的日常工作中,线程的核心线程数及最大线程数都是根据业务需要来设置的。一般最大线程数是核心线程的2倍。