疑惑
说到线程池,其实已经看过很多遍源码,不过大多是复用和阻塞队列获取时的部分,今天突然想到,线程池有个最大空闲时间,即空闲线程最大存活时间,我想知道怎么实现计时的,难道是每一个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);
终止线程;