分析&回答
ThreadPoolExecutor
中线程执行getTask()返回null,就会被回收。
我们来看看getTask方法是怎么处理的:
/**
* Performs blocking or timed wait for a task, depending on
* current configuration settings, or returns null if this worker
* must exit because of any of:
* 1. There are more than maximumPoolSize workers (due to
* a call to setMaximumPoolSize).
* 2. The pool is stopped.
* 3. The pool is shutdown and the queue is empty.
* 4. This worker timed out waiting for a task, and timed-out
* workers are subject to termination (that is,
* {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
* both before and after the timed wait, and if the queue is
* non-empty, this worker is not the last thread in the pool.
*
* @return task, or null if the worker must exit, in which case
* workerCount is decremented
*/
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling? // timed = true 表示存在非核心线程
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 这个if条件成立的前提:必须存在非核心线程
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null; // 这个null就是非核心线程被销毁的原因
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r; // 能取到任务就返回,否则设置timedOut进去死循环
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
结论
通过上面代码分析,可以得出结论:
- 线程数大于corePoolSize,当队列无task或者线程池状态异常时return null
- 线程数小于等于corePoolSize,getTask不会返回null,异常情况除外
你如果设置了keepAliveTime
并且设置executor.allowCoreThreadTimeOut(false)
,当线程池的数量超过corePoolSize时,超过的部分,多余即空闲的线程的存活时间超过keepAliveTime
就会被回收。
回收分两种场景:
1、未调用shutdown() ,RUNNING状态下全部任务执行完成的场景
线程数量大于corePoolSize,线程超时阻塞,超时唤醒后CAS减少工作线程数,如果CAS成功,返回null,线程回收。否则进入下一次循环。当工作者线程数量小于等于corePoolSize,就可以一直阻塞了。
2、调用shutdown() ,全部任务执行完成的场景
shutdown() 会向所有线程发出中断信号,这时有两种可能:
- 所有线程都在阻塞:中断唤醒,进入循环,都符合第一个if判断条件,都返回null,所有线程回收。
- 任务还没有完全执行完:至少会有一条线程被回收。在processWorkerExit(Worker w, boolean completedAbruptly)方法里会调用tryTerminate(),向任意空闲线程发出中断信号。所有被阻塞的线程,最终都会被一个个唤醒,回收。
反思&扩展
核心线程
所谓的核心线程与非核心线程,在线程池的数据结构中并没有明确区分,只要线程池剩余的线程数小于等于corePoolSize,那么剩下的线程都可以称为核心线程;
设置是否回收在保活时间后依然没没有任务执行核心线程
executor.allowCoreThreadTimeOut(true) 该属性缺省值为false
喵呜面试助手: 一站式解决面试问题,你可以搜索微信小程序 [喵呜面试助手] 或关注 [喵呜刷题] -> 面试助手 免费刷题。如有好的面试知识或技巧期待您的共享!