newCachedThreadPool如何缓存线程

时间:2013-04-22 08:28:33

标签: java multithreading executorservice java.util.concurrent

根据public static ExecutorService newCachedThreadPool()类中的方法Executor的评论:

Threads that have not been used for sixty seconds are terminated and 
removed from the **cache**.

我想知道缓存在哪里以及它是如何运作的?因为我在Collection或它的超类中没有看到任何可能的静态ThreadPoolExecutor变量。

3 个答案:

答案 0 :(得分:3)

技术上Worker是一个Runnable,其中包含对Thread的引用,而不是Thread的引用。

让我们深入研究这门课程的机制。

Executors.cachedThreadPool使用ThreadPoolExecutor

中的此构造函数
new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());

其中60s对应keepAliveTime时间。

工人增加/任务提交

从提交的RunnableFutureCallable创建Runnable 这传递给execute()方法。

execute方法尝试将任务插入workQueue,在我们的例子中是SynchronousQueue。由于SynchronousQueue的语义,这将失败并返回false    (坚持这个想法,当我们讨论缓存方面时,我们将重新审视这个问题)

此调用将继续addIfUnderMaximumPoolSize execute内的java.util.concurrent.ThreadPoolExecutor.Worker方法,该方法将创建Worker可运行并创建一个主题并将创建的workers添加到thread.start() hashSet。 (其他人在答案中提到过)

然后调用public void run() { try { Runnable task = firstTask; firstTask = null; while (task != null || (task = getTask()) != null) { runTask(task); task = null; } } finally { workerDone(this); } }

工人的跑步方法非常重要,应该注意。

run

此时您已提交任务并创建并运行该线程。

工人搬迁

runTask方法中,如果您注意到有一个while循环。 这是一段令人难以置信的有趣代码。

如果任务不为空,它将短路而不检查第二个条件。

使用getTask运行任务并且任务引用设置为null后,调用将进入第二个检查条件,该条件将其转换为workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS); 方法。

这是决定工人是否应该被清除的部分。

finally

在这种情况下,轮询workQueue一分钟,以检查队列中是否有新任务。 如果不是,它将返回null并检查worker是否可以退出。

返回null表示我们将暂时退出workerQueue.offer块。

这里将工作者从HashSet中删除,引用的Thread也消失了。

缓存方面

回到我们在任务提交中讨论的SynchronousQueue。

如果我提交的任务workerQueue.pollrun()能够协同工作,即在60年代之间有一个任务需要处理,我可以重新使用该线程。

如果我在每次任务执行期间将59s与61s的睡眠放在一起,可以看到这一点。

59s我可以看到线程被重用了。对于61s我可以看到在池中创建了一个新线程。

N.B。实际时间可能因机器而异,我的Thread.currentThread().getName()只是打印出{{1}}

如果我遗漏了某些内容或误解了代码,请在评论中告诉我。

答案 1 :(得分:1)

Cache这个词只是一个抽象。在内部,它使用HashSet来保存线程。根据代码:

/**
 * Set containing all worker threads in pool. Accessed only when
 * holding mainLock.
 */
private final HashSet<Worker> workers = new HashSet<Worker>();

如果你对submitexecute的runnable感兴趣的话。

newCachedThreadPool使用SynchronousQueue<Runnable>来处理它们。

答案 2 :(得分:0)

如果您查看ThreadPoolExecutor的代码,您会看到:

 /**
 * Set containing all worker threads in pool. Accessed only when
 * holding mainLock.
 */
private final HashSet<Worker> workers = new HashSet<Worker>();

和此:

/**
 * The queue used for holding tasks and handing off to worker
 * threads.  We do not require that workQueue.poll() returning
 * null necessarily means that workQueue.isEmpty(), so rely
 * solely on isEmpty to see if the queue is empty (which we must
 * do for example when deciding whether to transition from
 * SHUTDOWN to TIDYING).  This accommodates special-purpose
 * queues such as DelayQueues for which poll() is allowed to
 * return null even if it may later return non-null when delays
 * expire.
 */
private final BlockingQueue<Runnable> workQueue;

而且:

 try {
          Runnable r = timed ?
               // here keepAliveTime is passed as sixty seconds from
               // Executors#newCachedThreadPool()
               workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
               workQueue.take();
           if (r != null)
                return r;
           timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }

我真诚地介绍了实际的实现代码,记住这些指针将有助于您更清楚地理解。