105

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

但是,我最近添加了生成额外线程的功能,这样做使我超出了我设置的线程限制。在这种情况下,最好是猜测并增加我可以允许的线程数还是切换到 aCachedThreadPool这样我就不会浪费线程了?

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

4

3 回答 3

121

ACachedThreadPool似乎适合您的情况,因为直接将一个用于长时间运行的线程没有负面影响。java 文档中关于 CachedThreadPools 适合短任务的评论只是表明它们特别适合这种情况,而不是它们不能用于长时间运行的任务。

a 的主要问题CachedThreadPool是它会创建多达Integer.MAX_VALUE数量的线程,因为如果缓存中不存在未使用的线程,它总是会产生一个新线程。因此,如果您有长时间运行的任务,那么您更有可能增加并发线程的数量超过您的预期,因为这种类型的线程池不会限制有多少并发执行本身。如上所述,这对于您的用例来说似乎不是问题,但需要注意。

CachedThreadPool为了进一步详细说明 a和 a之间的区别FixedThreadPoolExecutors.newCachedThreadPoolExecutors.newFixedThreadPool都通过 的实例由相同的线程池实现(至少在开放的 JDK 中)支持ThreadPoolExecutor,只是参数不同。区别只是它们的线程最小值、最大值、线程终止时间和队列类型。

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>());
}

当您实际上想要使用固定数量的线程时, AFixedThreadPool确实有其优势,因为那时您可以向执行器服务提交任意数量的任务,同时知道线程数将保持在您指定的级别。如果您明确想要增加线程数,那么这不是合适的选择。

然而,这确实意味着您可能遇到的一个问题CachedThreadPool是关于限制并发运行的线程数。这CachedThreadPool不会为您限制它们,因此您可能需要编写自己的代码以确保您不会运行太多线程,您可以通过ThreadPoolExecutor使用所需的行为特征实例化您自己的线程来相对容易地做到这一点。这实际上取决于您的应用程序的设计以及如何将任务提交给执行器服务。

于 2013-07-30T21:50:38.660 回答
55

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

CachedThreadPool比更危险FixedThreadPool

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

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

既然你知道两者都是邪恶的,那么较小的邪恶就没有任何好处。首选ThreadPoolExecutor,它提供对许多参数的精细控制。

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

所以我有一个程序可以生成执行一堆任务的线程(~5-150)。

您确定您了解您的操作系统和所选硬件实际处理线程的方式吗?Java 如何将线程映射到 OS 线程,如何将线程映射到 CPU 线程等?我之所以问,是因为只有在下面有大量 CPU 内核/线程时,在 ONE JRE 中创建 150 个线程才有意义,而情况很可能并非如此。根据所使用的操作系统和 RAM,创建超过 n 个线程甚至可能导致您的 JRE 由于 OOM 错误而被终止。所以你应该真正区分线程和这些线程要做的工作,你甚至能够处理多少工作等等。

这就是 CachedThreadPool 的问题:在实际上无法运行的线程中排队长时间运行的工作是没有意义的,因为您只有 2 个 CPU 内核能够处理这些线程。如果你最终有 150 个调度线程,你可能会为 Java 和操作系统中使用的调度程序创建大量不必要的开销来同时处理它们。如果您只有 2 个 CPU 内核,这根本是不可能的,除非您的线程一直在等待 I/O 等。但即使在那种情况下,很多线程也会创建很多 I/O ......

而FixedThreadPool 不会出现这个问题,它是用例如2+n 个线程创建的,其中n 当然是合理的低,因为使用硬件和操作系统资源来管理无论如何都不能运行的线程的开销要少得多。

于 2016-06-16T12:02:46.497 回答