# 线程池线程复用的原理是什么

思考这么一个问题:任务结束后会不会回收线程?

答案是: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 循环,也就意味着当前工作线程被销毁

通过上面这个问题可以得出一个结论:当你的线程池参数配置合理的时候,执行完任务的线程是不会被销毁的,而是会从任务队列中取出任务继续执行!