java - newCachedThreadPool如何缓存线程

标签 java multithreading executorservice java.util.concurrent

根据 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 是根据提交的 CallableRunnable 创建的。
这将传递给 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.offerworkerQueue.poll 能够协同工作,即在这 60 秒之间有一个任务要处理我可以重新使用线程。

如果我在每次任务执行之间放置 59 秒和 61 秒的 hibernate 时间,这可以在实际操作中看到。

59 秒后,我可以看到线程被重新使用。对于 61 秒,我可以看到池中创建了一个新线程。

注意实际时间可能因机器而异,我的 run() 只是打印出 Thread.currentThread().getName()

如果我遗漏了什么或误解了代码,请在评论中告诉我。

关于java - newCachedThreadPool如何缓存线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16142567/

相关文章:

java - 连续运行ExecutorService任务

java - 高效地将无限数量的任务提交到 Java 线程池

java - ASP MVC5 UserManager 添加方法

ruby - 如何在线程中捕获异常

java - Java 多线程中线程状态如何工作?

c++ - QProcess:execute 阻止整个应用程序

java - 并行化 for 循环并填充多个数据结构

java - 属性类如何在 Java 中为其 'parent' 类调用方法?

java - 如何检查 SmbFile 中的流是否已准备好读取?

java - 边界椭圆