java-newCachedThreadPool如何缓存线程

根据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()

如果我遗漏了某些东西或误解了代码,请在评论中让我知道.

上一篇:用纯Java替换Akka Scheduler


下一篇:java-ExecutorService JVM不会终止