Java FixedThreadPool与CachedThreadPool:两个邪恶中的较小者

Java FixedThreadPool与CachedThreadPool:两个邪恶中的较小者,java,multithreading,threadpool,executorservice,java.util.concurrent,Java,Multithreading,Threadpool,Executorservice,Java.util.concurrent,我有一个程序,可以生成执行一系列任务的线程(~5-150)。最初,我使用了FixedThreadPool,因为我认为它们更适合长寿命的任务,而且我对多线程的了解非常有限,我认为线程的平均寿命(几分钟)“长寿命” 但是,我最近添加了生成额外线程的功能,这样做会使我超出设置的线程限制。在这种情况下,猜测并增加我可以允许的线程数,或者切换到CachedThreadPool以便我没有浪费的线程,这会更好吗 初步尝试这两种方法,似乎没有什么不同,因此我倾向于使用CachedThreadPool以避免浪费。

我有一个程序,可以生成执行一系列任务的线程(~5-150)。最初,我使用了
FixedThreadPool
,因为我认为它们更适合长寿命的任务,而且我对多线程的了解非常有限,我认为线程的平均寿命(几分钟)“长寿命”

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


初步尝试这两种方法,似乎没有什么不同,因此我倾向于使用
CachedThreadPool
以避免浪费。然而,线程的生命周期是否意味着我应该选择一个
FixedThreadPool
,只处理未使用的线程?使这些额外的线程看起来没有被浪费,但我希望得到澄清。

缓存线程池正是您在这种情况下应该使用的,因为对于长时间运行的线程使用缓存线程池不会产生负面后果。java文档中关于CachedThreadPool适用于短任务的注释仅表明它们特别适用于此类情况,而不是它们不能或不应该用于涉及长时间运行任务的任务

更详细地说,和都由相同的线程池实现(至少在开放JDK中)支持,只是参数不同。区别在于它们的线程最小值、最大值、线程终止时间和队列类型

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>());
}
publicstaticexecutorservice newFixedThreadPool(int-nThreads){
返回新的ThreadPoolExecutor(第n个线程,第n个线程,
0L,时间单位为毫秒,
新建LinkedBlockingQueue());
}
公共静态执行器服务newCachedThreadPool(){
返回新的ThreadPoolExecutor(0,Integer.MAX_值,
60升,时间单位。秒,
新建SynchronousQueue());
}
当您确实希望使用固定数量的线程时,FixedThreadPool确实有其优势,因为这样您就可以向executor服务提交任意数量的任务,同时知道线程数量将保持在您指定的级别。如果明确希望增加线程数,那么这不是合适的选择


但是,这并不意味着CachedThreadPool可能存在的一个问题是限制并发运行的线程数。CachedThreadPool不会为您限制这些线程,因此您可能需要编写自己的代码,以确保不会运行太多线程。这实际上取决于应用程序的设计以及任务如何提交给executor服务。

在高负载的应用程序中,
FixedThreadPool
CachedThreadPool
都是有害因素

CachedThreadPool
FixedThreadPool

如果您的应用程序是高负载的,并且要求低延迟,由于以下缺点,最好将这两个选项都去掉

  • 任务队列的无界性:它可能导致内存不足或高延迟
  • 长时间运行的线程将导致
    CachedThreadPool
    在创建线程时失控
  • 既然你知道两者都是邪恶的,那么较小的邪恶不会带来任何好处。首选,它在许多参数上提供粒度控制

  • 将任务队列设置为有界队列以获得更好的控制
  • 拥有正确的RejectionHandler-您自己的RejectionHandler或JDK提供的默认处理程序
  • 如果您在任务完成之前/之后有一些事情要做,请重写执行前(线程,可运行)和执行后(可运行,可丢弃)
  • 如果需要线程自定义,则重写
  • 在运行时动态控制线程池大小(相关SE问题:)
  • 因此,我有一个程序,可以生成执行一系列任务的线程(~5-150)

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

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


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

    有时没有更好的选择,您可以只有一个CPU核心,但是如果您运行的服务器上的每个用户请求都会触发一个线程来处理请求,那么就没有其他合理的选择了,特别是如果您计划在扩大用户基础后扩展服务器的话。@mFeinstein如果一个人在pos中,怎么可能没有选择呢