Java线程池如何定时回收空间线程探究


疑惑

说到线程池,其实已经看过很多遍源码,不过大多是复用和阻塞队列获取时的部分,今天突然想到,线程池有个最大空闲时间,即空闲线程最大存活时间,我想知道怎么实现计时的,难道是每一个worker类中一个计时器吗?

解惑

找了很多博客,都没有找到,只能自己在源码里找了,意外地好找,因为用到keepAliveTime这个变量,整个ThreadPoolExecutor.java中只有一个方法,只看这个方法就可以了,这个方法是getTask(),其中用到keepAliveTime的代码是这样的:

 try {
        Runnable r = timed ?workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
        if (r != null)
            return r;
        timedOut = true;
    } catch (InterruptedException retry) {
            timedOut = false;
    }

其中最重要的就是这句
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
有什么用呢,看一下workQueue是什么:
private final BlockingQueue<Runnable> workQueue;
一个阻塞队列,即 核心线程数 =< 当前线程数 && 当前线程数 < 最大线程数时,我们将任务封装放进一个阻塞队列,这就是那个阻塞队列;
然后看这个阻塞队列方法
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.
检索并删除此队列的头节点元素,如果需要,将等待到指定的等待时间,直到元素可用为止。

其实熟悉阻塞队列就应该知道取出元素的方法有两个poll()take(),前者是一个非阻塞方法,如果当前队列为空,直接返回,而take()是一个阻塞方法,即如果当前队列为空,阻塞线程,封装线程到AQS的条件变量的条件队列中,而上面的方法是一个介于二者之间的方法,语义是如果队为空,该方法会阻塞线程,但是有一个阻塞时间,如果到时见还没有被唤醒,就自动唤醒;

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


  目录