一.线程池的参数指的是什么
之前我们提到过线程池的实现类就是ThreadPoolExecutor,它是继承自AbstractExecutorService类的,实现了ExecutorService接口。
其实线程池的参数指的就是ThreadPoolExecutor的成员属性,这些属性决定了线程池本身。
二.七大参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
来到ThreadPoolExecutor的构造器,发现这里的七个参数,这时候我们不明白这些都是什么含义,先上一个总结图,然后再一个一个说明:
三.一个例子
银行有很多服务窗口,有时窗口是满的,有时部分窗口休息不开放。
将整个银行看成一个线程池,每个进入的客户就是一个个任务,工作人员就是线程池中的线程,那么:
corePoolSize 核心线程数:
corePoolSize可以理解为线程池中的常驻线程数量,也就是一直在上班的员工,当调用threadPool.execute方法,这些线程就会对任务队列中的一个一个任务进行操作。
maximumPoolSize 能容纳最大线程数:
这个可以理解为银行中柜台的个数,也就是线程池中的最大容量,当所有工作人员都在柜台开始工作,此时柜台已经被占满了。
workQueue 任务队列:被提交未执行
任务队列可以理解为正在排队的客户,银行一般都有很多椅子,这就是我们的任务队列,区别在于我们是拿号按照指定次序到对应窗口,而程序中是工作线程去找任务进行执行。
任务队列的分类:
- ArrayBlockingQueue
是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。 - LinkedBlockingQueue
一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列 - SynchronousQueue
一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool(5)使用了这个队列。 - PriorityBlockingQueue
一个具有优先级的无限阻塞队列。
threadFactory 线程工厂:
生成工作线程的工厂对象,一般用默认的即可
handler 拒绝策略:
当队列已满,且工作线程数大于等于maximumPoolSize,此时handler会说明如何执行多出来的Runnable任务。这就相当于,当银行柜台所有员工都在上班,且椅子都满了,如何处理这个客户的请求问题。
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
其本身是一个接口,定义的方法可以看出它就是要求处理Runnable任务的。JDK有四种默认拒绝策略:
keepAliveTime & TimeUnit:
TimeUnit其实就是Java8后引入的工具类,表示时间的。keepAliveTime定义了一个时间,这个时间是用来销毁多余的工作线程的,什么是多余的工作线程呢?
- 对应到银行的例子,其实就是今天本来有两个员工上班(核心线程),干着干着发现客户(RunnableTask)太多,于是就会通知其他员工来上班,等到其他加班的人空闲到一定时间,就让他们回家休息,这就是keepAliveTime。
四.线程池底层原理总结:
- 在创建了线程池后,开始等待请求
- 当调用execute()方法添加一个请求任务时,线程池会做出如下判断
- 如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务
- 如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列
- 如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务
- 如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略handler来执行
- 当一个线程完成任务时,它会从队列中取下一个任务来执行
- 当一个线程无事可做超过一定的时间(keepAliveTime)时,线程会判断:
如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。
所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。