# 线程池线程复用的原理是什么
思考这么一个问题:任务结束后会不会回收线程?
答案是:allowCoreThreadTimeOut 控制
/java/util/concurrent/ThreadPoolExecutor.java:1127 | |
final void runWorker(Worker w) { | |
Thread wt = Thread.currentThread(); | |
Runnable task = w.firstTask; | |
w.firstTask = null; | |
w.unlock(); // allow interrupts | |
boolean completedAbruptly = true; | |
try { | |
while (task != null || (task = getTask()) != null) {...执行任务...} | |
completedAbruptly = false; | |
} finally { | |
processWorkerExit(w, completedAbruptly); | |
} | |
} | |
首先线程池内的线程都被包装成了一个个的java.util.concurrent.ThreadPoolExecutor.Worker,然后这个worker会马不停蹄的执行任务,执行完任务之后就会在while循环中去取任务,取到任务就继续执行,取不到任务就跳出while循环(这个时候worker就不能再执行任务了)执行 processWorkerExit方法,这个方法呢就是做清场处理,将当前woker线程从线程池中移除,并且判断是否是异常的进入processWorkerExit方法,如果是非异常情况,就对当前线程池状态(RUNNING,shutdown)和当前工作线程数和当前任务数做判断,是否要加入一个新的线程去完成最后的任务(防止没有线程去做剩下的任务). | |
那么什么时候会退出while循环呢?取不到任务的时候(getTask() == null).下面看一下getTask方法 | |
private Runnable getTask() { | |
boolean timedOut = false; // Did the last poll() time out? | |
for (;;) { | |
int c = ctl.get(); | |
int rs = runStateOf(c); | |
//(rs == SHUTDOWN && workQueue.isEmpty()) || rs >=STOP | |
// 若线程池状态是 SHUTDOWN 并且 任务队列为空,意味着已经不需要工作线程执行任务了,线程池即将关闭 | |
// 若线程池的状态是 STOP TIDYING TERMINATED, 则意味着线程池已经停止处理任何任务了,不在需要线程 | |
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { | |
// 把此工作线程从线程池中删除 | |
decrementWorkerCount(); | |
return null; | |
} | |
int wc = workerCountOf(c); | |
//allowCoreThreadTimeOut: 当没有任务的时候,核心线程数也会被剔除,默认参数是 false, 官方推荐在创建线程池并且还未使用的时候,设置此值 | |
// 如果当前工作线程数 大于 核心线程数,timed 为 true | |
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; | |
//(wc> maximumPoolSize || (timed && timedOut)): 当工作线程超过最大线程数,或者 允许超时并且超时过一次了 | |
//(wc> 1 || workQueue.isEmpty ()): 工作线程数至少为 1 个 或者 没有任务了 | |
// 总的来说判断当前工作线程还有没有必要等着拿任务去执行 | |
//wc > maximumPoolSize && wc>1 : 就是判断当前工作线程是否超过最大值 | |
// 或者 wc > maximumPoolSize && workQueue.isEmpty (): 工作线程超过最大,基本上不会走到这, | |
// 如果走到这,则意味着 wc=1 , 只有 1 个工作线程了,如果此时任务队列是空的,则把最后的线程删除 | |
// 或者 (timed && timedOut) && wc>1: 如果允许超时并且超时过一次,并且至少有 1 个线程,则删除线程 | |
// 或者 (timed && timedOut) && workQueue.isEmpty (): 如果允许超时并且超时过一次,并且此时工作 队列为空,那么妥妥可以把最后一个线程(因为上面的 wc>1 不满足,则可以得出来 wc=1)删除 | |
if ((wc > maximumPoolSize || (timed && timedOut)) | |
&& (wc > 1 || workQueue.isEmpty())) { | |
if (compareAndDecrementWorkerCount(c)) | |
// 如果减去工作线程数成功,则返回 null 出去,也就是说 让工作线程停止 while 轮训,进行收尾 | |
return null; | |
continue; | |
} | |
try { | |
// 判断是否要阻塞获取任务 | |
Runnable r = timed ? | |
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : | |
workQueue.take(); | |
if (r != null) | |
return r; | |
timedOut = true; | |
} catch (InterruptedException retry) { | |
timedOut = false; | |
} | |
} | |
} | |
// 综上所述,如果 allowCoreThreadTimeOut 为 true, 并且在第 1 次阻塞获取任务失败了,那么当前 getTask 会返回 null, 不管是不是核心线程;那么 runWorker 中将推出 while 循环,也就意味着当前工作线程被销毁 |
通过上面这个问题可以得出一个结论:当你的线程池参数配置合理的时候,执行完任务的线程是不会被销毁的,而是会从任务队列中取出任务继续执行!