Java的ThreadPoolExecutor是很常用的线程池,其构造如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
在JDK携带的源码注释中可以很明确的看到各个参数的含义:
corePoolSize:保持在池中的线程数量,如果没有设置allowCoreThreadTimeout参数,即使这些线程空闲也将一直保持在池中
maximumPoolSize:线程池中的最大线程数量
keepAliveTime:当线程池中空闲线程数量超过corePoolSize时,多余的线程会超过这个保活时间会被销毁;
unit:keepAliveTime的单位
workQueue:任务队列,被添加到线程池中,但尚未被执行的任务;它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种;
threadFactory:线程工厂,用于创建线程,一般用默认即可;
handler:拒绝策略;当任务太多来不及处理时,如何拒绝任务,有4个策略:
- ThreadPoolExecutor.AbortPolicy():抛出java.util.concurrent.RejectedExecutionException异常
- ThreadPoolExecutor.CallerRunsPolicy:直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
- ThreadPoolExecutor.DiscardOldestPolicy():丢弃最老的任务
- ThreadPoolExecutor.DiscardPolicy:丢弃当前任务
当通过execute提交任务执行时:
- 如果当前线程数量小于corePoolSize,创建新的线程处理任务
- 如果当前线程数量等于corePoolSize,任务加入workQueue队列
- 如果线程池中数量大于等于corePoolSize,workQueue队列满,线程数量小于maximumPoolSize,创建新的线程处理任务
- 如果线程池中线程数量等于maximumPoolSize,workQueue队列也满,根据handler指定的策略处理任务
- 如果线程池中线程数量大于corePoolSize,空闲线程超过keepAliveTime,线程将被终止
因此,当并发任务提交时,
一开始,并发量不大,少量线程(<=corePoolSize)可以处理时,此时workQueue为空,线程池中线程数量<=corePoolSize
并发量继续增大,少量线程已经处理不了时,任务入队列,继续用corePoolSize数量的线程处理任务;
如果并发量没有变化,一直持续,队列一直累积,直到队列满,创建新的线程处理任务
如果并发量不是很大,创建的新的线程在完成新任务之后还能处理队列中的任务,那么队列会逐渐减少,直至为0,然后线程池中的线程也会有空闲,终止,最终线程数量降下来
如果并发量很大,创建新的线程也处理不过来,那么这时候就会出现线程池满,队列也满的情况,此时就会按照拒绝策略进行处理。
特殊情况:
- corePoolSize为0时怎么处理?默认会起一个线程进行处理。
- 队列容量为空怎么处理?意味着一旦线程数量达到corePoolSize,就要new新线程处理任务,达到maximumPoolSize,就要按照拒绝策略进行处理
- 队列容量为无限大怎么处理?意味着队列永远不会满(直到内存不足崩溃),永远用corePoolSize数量的线程处理任务,如果处理不过来的话,队列就会一直增长直到崩溃