我们从事Java开发的,或多或少都接触过Java线程和线程池。本文主要是对Java线程池类ThreadPoolExecutor的一些用法归纳和总结。
我们先来看如下类图:
ThreadPoolExecutor提供了4个不同参数的构造器,通过源码可以知道,ThreadPoolExecutor最终执行的是带7个完整参数的构造器,如下:
corePoolSize 线程池中的核心线程数量,默认情况下创建后会一直保持在线程池中
maximumPoolSize 线程池中允许的最大线程数量
keepAliveTime 空闲线程存活时间,超过核心线程数量的空闲线程指定的最大存活时间,超过这个值线程会被终止
unit 空闲线程存活时间单位
workQueue 工作队列,缓存要执行的工作任务。一个新任务给线程池执行时,首先会判断当前线程池的核心线程数量,如果小于corePoolSize,则会直接创建一个核心线程处理该任务。
其它情况,在工作队列未满时,任务会被插入到工作队列尾部
threadFactory 线程工厂,线程池中线程创建时使用工厂
handler 指定拒绝策略
池程池的工作队列
通过源码我们知道,线程池的workQueue是一个BlockingQueue<Runnable>接口,在jdk中提供几种常用的实现。
ArrayBlockingQueue基于对象数组实现的有界阻塞队列,元素按FIFO排序。新任务插入到队列的尾部,获取任务从队列头部检索。
LinkedBlockingQueue基于链表实现的阻塞队列,默认列队大小为Integer.MAX_VALUE。元素按FIFO排序。新任务插入到队列的尾部,获取任务从队列头部检索。
SynchronousQueue 同步阻塞队列,没有缓存容量,每个插入操作必须等另个线程进行相应的删除操作。
PriorityBlockingQueue 无界的阻塞队列,可以实现优先级,优先级通过指定Comparator来实现,元素优先级最低为队列的头部第一个元素,内部队列为对象数组。
线程池的拒绝策略当工作队列中的任务已经达到最大容量,并且线程已达到最大限制时,此时有新任务,将会执行拒绝策略。
RejectedExecutionHandler接口,jdk默认提供以下几种实现
AbortPolicy 丢弃任务,抛出异常RejectedExecutionException
CallerRunsPolicy 线程池未关闭时,直接在调用者线程中执行任务的run方法
DiscardPolicy 丢弃任务,其它什么都不做
DiscardOldestPolicy 丢弃工作队列中最早的那个任务,把被拒绝的任务放入工作队列
线程池的execute方法在使用线程池时,都是调用execute来执行任务或将任务提交到工作队列中的
通过以上的源码,我们可以看到。
代码1366行,会判断当前线程池中的工作线程数量是否小于corePoolSize,满足条件会调用addWorker方法,在addWorker中执行启动新的核心线程并直接处理该任务。
如果workerCount>=corePoolSize并且线程池在运行中,则会调用workQueue.offer方法将任务添加到工作队列尾部(见:代码1371行)。
如果workQueue.offer返回false(有限阻塞队列时),会执行代码1378行,即调用addWorker指定参数core=false并判断返回结果,在addWorker中会判断当前线程池中的工作线程数量是否大于maximumPoolSize;如果成立说明线程池允许的最大线程数量已达上限。将会执行拒绝策略(见:代码1397行),其它情况会创建启动新线程来执行任务。
综合上述,整个执行过程如下图:
Java线程池提供了shutdown和shutdownNow两个方法关闭线程池。
shutdown 中断空闲线程,线程池不能接收任何新的任务,调用前已在工作队列中的任务会继续执行。
shutdownNow 立即关闭,尝试终止正在执行的任务,停止正在等待执行的任务,并返回等待执行的任务列表。