vlambda博客
学习文章列表

线程池中线程是如何知道自己达到 keepAliveTime 时间,然后销毁的?


点击蓝色小字关注!  关注一下你就不会有bug!

下面是 ThreadPoolExecutor 源码的 getTask() 方法部分:

 1private Runnable getTask() {
2    boolean timedOut = false// Did the last poll() time out?
3
4    for (;;) {
5        int c = ctl.get();
6        int rs = runStateOf(c);
7
8        // Check if queue empty only if necessary.
9        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
10            decrementWorkerCount();
11            return null;
12        }
13
14        int wc = workerCountOf(c);
15
16        // Are workers subject to culling?
17        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
18
19        if ((wc > maximumPoolSize || (timed && timedOut))
20            && (wc > 1 || workQueue.isEmpty())) {
21            if (compareAndDecrementWorkerCount(c))
22                return null;
23            continue;
24        }
25
26        try {
27            //!!!!重点!!!!!
28            Runnable r = timed ?
29                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
30                workQueue.take();
31            if (r != null)
32                return r;
33            timedOut = true;
34        } catch (InterruptedException retry) {
35            timedOut = false;
36        }
37    }
38}

上面的代码重点是 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();。

timed 参数是用来判断当前线程数是否大于核心线程数,如果大于,就执行 poll 方法,否则执行 take 方法。因为这些大于核心线程数的线程如果没有任务是有可能被回收处理的,而 take() 方法会使线程一直被阻塞住,直到有任务来,不会对线程进行回收。

workQueue 是定义的阻塞队列,阻塞队列的两个取出元素的方法 poll() 和 take(),前者是一个非阻塞方法,如果当前队列为空直接返回,而 take() 是一个阻塞方法,即如果当前队列为空阻塞线程,封装线程到 AQS 的条件变量的条件队列中,而上面的方法是一个介于二者之间的方法

然后看这个阻塞队列方法 E poll(long timeout, TimeUnit unit),其注释如下:

Retrieves and removes the head of this queue, waiting up to the specified wait time if necessary for an element to become available.

所以我们的线程在获取任务时,如果队列中已经没有任务会在此处阻塞 keepALiveTime 的时间,如果到时间都没有任务就会 return null(不是直接返回null,是最终),然后在 runWorker() 方法中执行 processWorkerExit(w, completedAbruptly) 终止线程。


往期精彩回顾


点击左下角阅读原文查看历史经典技术问题汇总,右下角素质三连呀