FixedThreadPool vs CachedThreadPool:两害相权取其轻

我有一个程序,可以生成线程(~5-150),这些线程执行一堆任务。最初,我使用一个,因为这个类似的问题表明它们更适合于更长的任务,并且由于我对多线程的了解非常有限,我认为线程的平均寿命(几分钟)“长寿命”。FixedThreadPool

但是,我最近添加了生成其他线程的功能,这样做会使我超过我设置的线程限制。在这种情况下,是猜测并增加我可以允许的线程数还是切换到a以便我没有浪费线程更好?CachedThreadPool

初步尝试它们,似乎没有区别,所以我倾向于使用just以避免浪费。但是,线程的生命周期是否意味着我应该选择一个并只处理未使用的线程?这个问题使这些额外的线程看起来没有被浪费,但我希望得到澄清。CachedThreadPoolFixedThreadPool


答案 1

A 似乎适合您的情况,因为直接对长时间运行的线程使用一个线程不会产生负面影响。Java文档中关于CachedThreadPools适用于短期任务的评论只是表明它们特别适合这种情况,而不是它们不能用于长时间运行的任务。CachedThreadPool

a 的主要问题是,它将创建最多数量的线程,因为如果缓存中不存在未使用的线程,它将始终生成新线程。因此,如果您有长时间运行的任务,则更有可能将并发线程数增加到比所需数量更多的数量,因为这种类型的线程池不会限制并发执行的数量。对于您的用例来说,这似乎不是所描述的问题,但这是需要注意的。CachedThreadPoolInteger.MAX_VALUE

为了进一步阐述 a 和 a 之间的区别,Executors.newCachedThreadPoolExecutors.newFixedThreadPool 都由相同的线程池实现(至少在打开的 JDK 中)通过 一个实例支持,只是具有不同的参数。区别只是它们的线程最小值,最大值,线程终止时间和队列类型。CachedThreadPoolFixedThreadPoolThreadPoolExecutor

public static ExecutorService newFixedThreadPool(int nThreads) {
     return new ThreadPoolExecutor(nThreads, nThreads,
                                   0L, TimeUnit.MILLISECONDS,
                                   new LinkedBlockingQueue<Runnable>());
 }

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                 60L, TimeUnit.SECONDS,
                                 new SynchronousQueue<Runnable>());
}

当您确实想要使用固定数量的线程时,A确实有其优势,从那时起,您可以将任意数量的任务提交给执行器服务,同时知道线程数量将保持在您指定的级别。如果显式想要增加线程数,则这不是合适的选择。FixedThreadPool

但是,这确实意味着您可能遇到的一个问题是限制并发运行的线程数。不会限制它们,因此您可能需要编写自己的代码来确保不会运行太多线程,您可以通过使用所需的行为特征实例化自己的线程来相对轻松地完成此操作。这实际上取决于应用程序的设计以及将任务提交到执行程序服务的方式。CachedThreadPoolCachedThreadPoolThreadPoolExecutor


答案 2

在高负载应用程序中,两者都是邪恶的。FixedThreadPoolCachedThreadPool

CachedThreadPoolFixedThreadPool

如果您的应用程序负载高且要求低延迟,则由于以下缺点,最好摆脱这两个选项

  1. 任务队列的无限性质:可能导致内存不足或高延迟
  2. 长时间运行的线程将导致线程创建时失控CachedThreadPool

因为你知道两者都是恶,那么较小的恶不会有任何好处。首选 ThreadPoolExecutor,它对许多参数提供精细控制。

  1. 将任务队列设置为有界队列以更好地控制
  2. 拥有正确的拒绝处理程序 - JDK 提供的您自己的拒绝处理程序或默认处理程序
  3. 如果您在任务完成之前/之后有事情要做,请覆盖和beforeExecute(Thread, Runnable)afterExecute(Runnable, Throwable)
  4. 覆盖线程工厂(如果需要自定义线程)
  5. 在运行时动态控制线程池大小(相关的 SE 问题:动态线程池)