一、Java源码定义
首先我们来看下jdk所定义的ThreadPoolExecutor接口信息
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数
- corePoolSize:线程池中需要保持的核心线程数,即使这些线程处于空闲状态
- maxmumPoolSize:线程池中所允许的最大线程数
- keepAliveTime:当池中线程数超过核心线程数时,超出部分线程消亡前的空闲时间
- unit:keeepAliveTime的时间单元
- workQueue:由execute方法所提交的Runnable任务在未执行前所存储的队列
- threadFactory:创建线程的线程创建工厂
- handler:拒绝策略-线程池满了&队列也满了
二、实现原理
线程池启动后默认是没有线程的,当有task时才去创建线程来执行。随着task增加,线程池中的线程数达到corepoolsize时,线程数不再增加,新提交的task加入到workQueue中。随着task继续增加,workQueue队列满时(workQueue为有界队列)线程池会继续创建新的线程,直至线程数达到maxmumPoolSize。如果task继续提交,此时线程池将依据handler策略对新提交的task执行拒绝操作。洪峰过后,当线程池中线程处于空闲状态时间超过keepAliveTime时,线程消亡,直至线程数减少到corePoolSize。 上诉即整个线程池运行流程,此处需要说明,如果线程池队列为无界队列,而提交任务又无节制的话,洪峰来临时,很容易出现OOM。
三、任务缓存队列
workQueue的类型分为以下三种:
- ArrayBlockingQueue:基于数组的FIFO有界队列。
- LinkedBlockingQueue:基于链表的FIFO有界队列,默认队列长度为Integer.MAX_VALUE。
- SynchronousQueue:此队列并不保存新提交的task,而是直接创建新的线程来执行task。
四、拒绝策略
上诉的RejectedExecutionHandler通常有以下几种:
- AbortPolicy:对齐task,抛出RejectedExecutionException。
- CallerRunsPolicy:线程池未shut down时,交由task提交线程直接来执行。
- DiscardOldestPolicy:丢弃workQueue里最旧的task,然后重新执行。
- DiscardPolicy:静静的丢弃当前task,此处未抛异常。
五、关闭线程池
线程池提供了两种关闭方式,如下:
- shutdown:优雅关闭方式,此种方式线程池不再接受新的task,等队列里所有task执行完后关闭。
- shutdownNow:暴力关闭方式,此种方式terminate正在执行的task,clear任务队列,并返回未执行成功的task。
六、线程池监控
通常我们需要监控线程池的状态以及相关信息,可以获取以下几个参数来判断线程池所运行的信息:
- getActiveCount:当前正在执行任务的线程数。
- getCompletedTaskCount:返回已经完成的任务数(近似值)。
- getLargestPoolSize:线程池曾经创建过的最大的线程数。
- getPoolSize:返回当前线程池线程数。
- getTaskCount:返回所有提交到线程池的任务数。
七、线程池大小配置
线程池的大小配置基于cpu以及task类型来调节,不过基准参考可以参考如下:
- 如若task为CPU密集型可以配置CPU数+1。
- 如若task为IO密集型可以配置CPU数*2。 然后再基于实际task运行情况来微调线程池的大小。