线程池如何回收多余的线程的呢,首先我们要知道几个基本的知识
一:线程池状态之间的转换
状态 | 含义 |
---|---|
RUNNING | 线程池的初始化状态是RUNNING, 线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理 |
SHUTDOWN | 线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务,异步中断闲置的的线程,调用线程池的 shutdown() 接口时,线程池由RUNNING -> SHUTDOWN |
STOP | 线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。用线程池的 shutdownNow() 接口时,线程池由 (RUNNING or SHUTDOWN ) -> STOP |
TIDYING | 当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。 当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。 当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING |
TERMINATED | 线程池彻底终止,就变成TERMINATED状态。 线程池处在TIDYING状态时,执行完 terminated()之后,就会由 TIDYING -> TERMINATED |
二:线程池状态的值
代码语言:javascript复制private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
三:什么时候会进行回收线程池的线程
正如上图代码所示,当获取task任务为null的时候,就会执行processWorkerExit进行回收线程,因此关键就是上面时候task=null
四:task什么时候为null,即取不到任务
如图所示,返回null的情况有两种,如下伪代码所示
代码语言:javascript复制第一种情况
if(状态不是running&&(状态等于stop,tidying,terminated||队列为空))
第二种情况
boolean timeOuted=false;默认值
boolean allowCoreThreadTimeOut默认是falase,代表核心线程不会超时,即核心线程不会回收true,否则所有工作线程都有可能回收
boolean timed=allowCoreThreadTimeOut||当前线程是否大于核心线程
if((当前线程数大于线程池最大线程数||(timed&&当前线程是否超时))&&(还有工作线程||队列为空) )
五:我们按照场景回收线程池线程
未调用shutdown() ,RUNNING状态下全部任务执行完成的场景
假设此时核心线程为4,最大线程为8,一开始线程是4个,当阻塞队列满了之后,增加到8个,然后把阻塞队列搞完了,就会减少到4个(取决于allowCoreThreadTimeOut的值,这里讨论默认值false的情况,即核心线程不会超时。如果为true,工作线程可以全部销毁),我们默认allowCoreThreadTimeOut=false
第一次循环,此时线程的状态是running状态,因此条件1不能满足
进入到条件二,timeOut=false,timed=true,因此也不能满足条件二
则进入获取任务的方法,由于timed=true,则调用workQueue.poll
代码语言:javascript复制try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
阻塞队列没有任务返回null,此时设置超时时间timedOut=true
进入第二次循环,同理条件1不能满足
进入条件二,timed=true,timedOut=true,且队列尾空,则满足,跳出循环返回null,最后这个线程就会被移除
调用shutdown()
这种场景,无论是核心线程还是非核心线程都会被回收,调用shutdown就会发送中断信号给所有的空闲线程
最终传入false,调用下面这个方法。
可以看出,在发出中断信号前,会判断当前线程是否已经中断,以及要获得工作线程的独占锁。即让当前任务执行完之后,再发送中断信号
不管怎么样,最后都会在进入getask方法中然后返回null,最终调用方法processWorkExit进行回收
然后对应工作线程回收的场景,无非就是下面几种情况
- 任务已经全部完成,线程在阻塞等待,然后中断唤醒,进入循环,符合getTask中条件1,最终返回null,然后进行回收
- 任务还没有执行完成,且任务很多,线程被唤醒中断的时候,阻塞队列会调用Thread.interrupted方法,判断是中断转态,抛出异常,会被try cathc掉,然后重置线程转态,再次进行循环获取任务,不影响任何事情
- 任务还没有执行完成,但是任务不多了,比如有4个工作线程,有2个任务,其中两个工作线程阻塞,另外2个执行任务,执行完之后进入循环,符合getTask中条件1,返回null,这里至少有一条工作线程会被回收,最后会调用tryTerminate,向任意空闲线程发送中断,阻塞的线程最终都会被回收.