根据Executor类中方法public static ExecutorService newCachedThreadPool()的注释:
Threads that have not been used for sixty seconds are terminated and
removed from the **cache**.
我想知道缓存在哪里以及它如何运行?因为我在ThreadPoolExecutor或它的超类中没有看到任何可能的静态Collection变量.
解决方法:
从技术上讲,Worker是一个Runnable,其中包含对线程的引用,而不是对线程本身的引用.
让我们更深入地研究此类课程的技巧.
Executors.cachedThreadPool使用ThreadPoolExecutor中的此构造函数
new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
其中60s对应于keepAliveTime时间.
工人增加/任务提交
从提交的Callable或Runnable中创建一个RunnableFuture.
这被传递给execute()方法.
execute方法尝试将任务插入到工作队列中,在本例中为SynchronousQueue.由于SynchronousQueue的语义,这将失败并返回false.
(只要坚持这个想法,当我们谈论缓存方面时,我们将重新审视它)
调用继续执行过程中的addIfUnderMaximumPoolSize方法,该方法将创建一个可运行的java.util.concurrent.ThreadPoolExecutor.Worker并创建一个Thread并将创建的Worker添加到worker hashSet中. (其他人在答案中提到)
然后调用thread.start().
Worker的运行方法非常重要,应注意.
public void run() {
try {
Runnable task = firstTask;
firstTask = null;
while (task != null || (task = getTask()) != null) {
runTask(task);
task = null;
}
} finally {
workerDone(this);
}
}
此时,您已经提交了一个任务,并创建了一个线程并运行它.
罢工
在run方法中,如果您注意到有一个while循环.
这是一段非常有趣的代码.
如果任务不为空,它将短路并且不检查第二种情况.
使用runTask运行任务并将任务引用设置为null后,调用进入第二个检查条件,该条件将其带入getTask方法.
这是决定是否应该清除工人的部分.
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
在这种情况下,将轮询workQueue一分钟,以检查队列中是否有新任务.
如果不是,它将返回null并检查worker是否可以退出.
返回null意味着我们将退出while并进入finally块.
在这里,工作者从HashSet中删除,引用的线程也消失了.
缓存方面
回到我们在任务提交中讨论的SynchronousQueue.
如果我提交了一个任务,其中workerQueue.offer和workerQueue.poll可以协同工作,即在60年代之间有一个任务需要处理,我可以重用该线程.
如果我在每次执行任务之间都睡了59s vs 61s,就可以看到这一点.
持续59秒钟,我可以看到该线程被重新使用. 61秒钟后,我可以看到池中创建了一个新线程.
N.B.实际时间可能因机器而异,我的run()只是打印出Thread.currentThread().getName()
如果我遗漏了某些东西或误解了代码,请在评论中让我知道.