根据 Executor
类中方法 public static ExecutorService newCachedThreadPool()
的注释:
Threads that have not been used for sixty seconds are terminated and
removed from the **cache**.
我想知道缓存在哪里以及它是如何工作的?因为我在 ThreadPoolExecutor
或其父类(super class)中没有看到任何可能的静态 Collection
变量。
最佳答案
从技术上讲,Worker
是一个 Runnable
,包含对 Thread
的引用,而不是一个 Thread
本身。
让我们更深入地研究这个类的机制。
Executors.cachedThreadPool
使用来自 ThreadPoolExecutor
的构造函数
new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
其中 60s 对应于 keepAliveTime
时间。
worker 添加/任务提交
RunnableFuture
是根据提交的 Callable
或 Runnable
创建的。
这将传递给 execute()
方法。
execute
方法尝试将任务插入到 workQueue
中,在我们的例子中是 SynchronousQueue
。由于 SynchronousQueue
的语义,这将失败并返回 false。
(先抱住这个想法,等我们说到缓存方面再说)
继续调用 execute
中的 addIfUnderMaximumPoolSize
方法,这将创建一个 java.util.concurrent.ThreadPoolExecutor.Worker
runnable 并创建一个线程并将创建的 Worker
添加到 workers
hashSet。 (其他人在答案中提到的)
然后它调用 thread.start()
。
Worker的run方法很重要,需要注意。
public void run() {
try {
Runnable task = firstTask;
firstTask = null;
while (task != null || (task = getTask()) != null) {
runTask(task);
task = null;
}
} finally {
workerDone(this);
}
}
此时您已经提交了一个任务,并且创建并运行了一个线程。
worker 解雇
在 run
方法中,如果您注意到有一个 while 循环。
这是一段非常有趣的代码。
如果任务不为空,它将短路并且不检查第二个条件。
一旦任务使用 runTask
运行并且任务引用设置为 null,调用就会进入第二个检查条件,将其带入 getTask
方法。
这是决定 worker 是否应该被清除的部分。
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
在这种情况下,workQueue 会被轮询一分钟,以检查队列中是否有任何新任务。 如果不是,它将返回 null 并检查 worker 是否可以退出。
返回 null 意味着我们将跳出 while 并到达 finally
block 。
这里 worker 从 HashSet 中移除,引用的 Thread 也消失了。
缓存方面
回到我们在任务提交中讨论的 SynchronousQueue。
如果我提交了一个任务,其中 workerQueue.offer
和 workerQueue.poll
能够协同工作,即在这 60 秒之间有一个任务要处理我可以重新使用线程。
如果我在每次任务执行之间放置 59 秒和 61 秒的 hibernate 时间,这可以在实际操作中看到。
59 秒后,我可以看到线程被重新使用。对于 61 秒,我可以看到池中创建了一个新线程。
注意实际时间可能因机器而异,我的 run()
只是打印出 Thread.currentThread().getName()
如果我遗漏了什么或误解了代码,请在评论中告诉我。
关于java - newCachedThreadPool如何缓存线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16142567/