Java ThreadPoolExecutor
@author:Jingdai
@date:2020.11.03
最近看《Java并发编程实战》这本书,书中经常出现
ThreadPoolExecutor
,而自己对这个类不是很熟悉,学习了一下,简要总结一下。
基本概念
ThreadPoolExecutor
这个类是一个线程池,之前看《 Java核心技术·卷1》这本书,里面有介绍可以用Executors
类中的静态方法创建线程池,比如Executors.newCachedThreadPool()
方法,其实它本质也是调用ThreadPoolExecutor
的构造方法来创建线程池的,后面会介绍。但是看了Alibaba的Java开发手册,不允许使用Executors
类来创建线程池,如图。
所以还是非常有必要学习一下
ThreadPoolExecutor
类的使用的。
构造函数参数
ThreadPoolExecutor
类有4个构造方法来创建线程池。分别如下:
- ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)
- ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler)
- ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory)
- ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
现分别对各个参数进行简要介绍,这里先介绍
corePoolSize
、maximumPoolSize
和workQueue
三个参数,因为他们是相互关联的。
corePoolSize
:线程池的核心线程数
maximumPoolSize
:线程池的最大线程数
workQueue
:线程池使用的阻塞队列,这里主要关注它的长度首先假设
corePoolSize
、maximumPoolSize
和workQueue
的长度都是一个大于 0 的整数。在一个新的任务task
到来时, 分4种情况,如下图。
- 当线程池中线程数 <
corePoolSize
,那么就创建一个新的核心线程,并将任务交给它去执行。- 当线程池中线程数 >=
corePoolSize
,并且阻塞队列workQueue
没有满,则将新的任务放入阻塞队列中去等待。- 当线程池中线程数 >=
corePoolSize
且线程池中线程数 <maximumPoolSize
,同时阻塞队列workQueue
已经满了,则将创建一个新的非核心线程,并将任务交给它去执行。- 当线程池中线程数 =
maximumPoolSize
,同时阻塞队列workQueue
已经满了,则该任务会被拒绝,具体的拒绝处理方式稍后会介绍。注意:当设置的阻塞队列
workQueue
是无限队列时(如没有预定义容量的LinkedBlockingQueue),则maximumPoolSize
将没有用处。因为当超过corePoolSize
时,任务总会加到阻塞队列中,而阻塞队列不会满,所以永远不会创建非核心线程。
keepAliveTime
:线程保持活跃时间
unit
:线程保持活跃时间的单位如果线程池中的线程数量超过
corePoolSize
,则当多余的线程空闲时间超过上面参数设置的时间时,它将会被销毁。默认情况下,仅当线程池中线程数超过corePoolSize
时,这个策略才会执行,但是可以通过allowCoreThreadTimeOut(boolean)
方法来设置该策略对核心线程同样适用。
handler
:拒绝任务处理器当线程池关闭或线程池容量满了(
workQueue
满且池中线程数为maximumPoolSize
)时,再向线程池中提交任务时,将会引发拒绝任务处理器工作。有4种预定义的拒绝处理器。
ThreadPoolExecutor.AbortPolicy
:默认的拒绝处理器,当拒绝任务时抛出一个RejectedExecutionException
异常。ThreadPoolExecutor.CallerRunsPolicy
:让提交任务的线程自己去执行提交的任务。比如main线程给线程池提交一个任务被线程池拒绝了,则main线程自己执行这个任务。ThreadPoolExecutor.DiscardPolicy
:直接丢弃无法执行的任务。ThreadPoolExecutor.DiscardOldestPolicy
:如果线程池没有关闭,丢弃线程池中阻塞队列workQueue
的第一个任务,然后重新尝试执行。
其他相关方法
上面介绍的是构造函数的相关参数,同时,在线程池创建完成后,相关参数也是可以改变的。
getCorePoolSize()
和setCorePoolSize(int corePoolSize)
从名字可以看出,可以查看和修改线程池的核心线程数
getMaximumPoolSize()
和setMaximumPoolSize(int maximumPoolSize)
查看和修改线程池最大的线程数
setKeepAliveTime(long time, TimeUnit unit)
设置线程多长时间不活跃就会被销毁
其他
上面提到
Executors
的静态方法创建线程池其本质也是调用了ThreadPoolExecutor
的构造方法,看一下 jdk8 的源码。public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
这里会发现其本质都是这样,所以通过这个构造方法创建线程池可以更加清楚的知道线程池的各个参数。
参考
- Java 8 API