Java Executors.newCachedThreadPool() 与 Executors.newFixedThreadPool()

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/949355/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 21:26:41  来源:igfitidea点击:

Executors.newCachedThreadPool() versus Executors.newFixedThreadPool()

javamultithreadingconcurrencyexecutorservicethreadpoolexecutor

提问by hakish

newCachedThreadPool()versus newFixedThreadPool()

newCachedThreadPool()相对 newFixedThreadPool()

When should I use one or the other? Which strategy is better in terms of resource utilization?

我什么时候应该使用其中一种?哪种策略在资源利用方面更好?

采纳答案by bruno conde

I think the docs explain the difference and usage of these two functions pretty well:

我认为文档很好地解释了这两个函数的区别和用法:

newFixedThreadPool

newFixedThreadPool

Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue. At any point, at most nThreads threads will be active processing tasks. If additional tasks are submitted when all threads are active, they will wait in the queue until a thread is available. If any thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks. The threads in the pool will exist until it is explicitly shutdown.

创建一个线程池,该线程池重用固定数量的线程在共享的无界队列中运行。在任何时候,最多 nThreads 个线程将是活动的处理任务。如果在所有线程都处于活动状态时提交了其他任务,它们将在队列中等待,直到有线程可用。如果任何线程在关闭前的执行过程中因失败而终止,则在需要执行后续任务时,将有一个新线程取代它。池中的线程将一直存在,直到它被明确关闭。

newCachedThreadPool

newCachedThreadPool

Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available. These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks. Calls to execute will reuse previously constructed threads if available. If no existing thread is available, a new thread will be created and added to the pool. Threads that have not been used for sixty seconds are terminated and removed from the cache. Thus, a pool that remains idle for long enough will not consume any resources. Note that pools with similar properties but different details (for example, timeout parameters) may be created using ThreadPoolExecutor constructors.

创建一个线程池,根据需要创建新线程,但在可用时将重用先前构造的线程。这些池通常会提高执行许多短期异步任务的程序的性能。如果可用,调用 execute 将重用先前构造的线程。如果没有可用的现有线程,则会创建一个新线程并将其添加到池中。60 秒内未使用的线程将被终止并从缓存中删除。因此,保持空闲足够长时间的池不会消耗任何资源。请注意,可以使用 ThreadPoolExecutor 构造函数创建具有相似属性但细节不同(例如,超时参数)的池。

In terms of resources, the newFixedThreadPoolwill keep all the threads running until they are explicitly terminated. In the newCachedThreadPoolThreads that have not been used for sixty seconds are terminated and removed from the cache.

在资源方面,newFixedThreadPool将保持所有线程运行,直到它们被明确终止。在newCachedThreadPool60 秒内未使用的线程被终止并从缓存中删除。

Given this, the resource consumption will depend very much in the situation. For instance, If you have a huge number of long running tasks I would suggest the FixedThreadPool. As for the CachedThreadPool, the docs say that "These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks".

鉴于此,资源消耗将在很大程度上取决于情况。例如,如果您有大量长时间运行的任务,我建议您使用FixedThreadPool. 至于CachedThreadPool,文档说“这些池通常会提高执行许多短期异步任务的程序的性能”。

回答by Dhanaraj Durairaj

You must use newCachedThreadPool only when you have short-lived asynchronous tasks as stated in Javadoc, if you submit tasks which takes longer time to process, you will end up creating too many threads. You may hit 100% CPU if you submit long running tasks at faster rate to newCachedThreadPool (http://rashcoder.com/be-careful-while-using-executors-newcachedthreadpool/).

仅当您有 Javadoc 中所述的短期异步任务时,才必须使用 newCachedThreadPool,如果您提交需要较长时间处理的任务,您最终将创建太多线程。如果您以更快的速度向 newCachedThreadPool ( http://rashcoder.com/be-careful-while-using-executors-newcachedthreadpool/)提交长时间运行的任务,您可能会达到 100% CPU 。

回答by Prashant Gautam

That's right, Executors.newCachedThreadPool()isn't a great choice for server code that's servicing multiple clients and concurrent requests.

没错,Executors.newCachedThreadPool()对于为多个客户端和并发请求提供服务的服务器代码来说,这不是一个很好的选择。

Why? There are basically two (related) problems with it:

为什么?它基本上有两个(相关的)问题:

  1. It's unbounded, which means that you're opening the door for anyone to cripple your JVM by simply injecting more work into the service (DoS attack). Threads consume a non-negligible amount of memory and also increase memory consumption based on their work-in-progress, so it's quite easy to topple a server this way (unless you have other circuit-breakers in place).

  2. The unbounded problem is exacerbated by the fact that the Executor is fronted by a SynchronousQueuewhich means there's a direct handoff between the task-giver and the thread pool. Each new task will create a new thread if all existing threads are busy. This is generally a bad strategy for server code. When the CPU gets saturated, existing tasks take longer to finish. Yet more tasks are being submitted and more threads created, so tasks take longer and longer to complete. When the CPU is saturated, more threads is definitely not what the server needs.

  1. 它是无限的,这意味着您可以通过简单地向服务注入更多工作(DoS 攻击)来为任何人削弱您的 JVM 敞开大门。线程消耗不可忽略的内存量,并且还会根据正在进行的工作增加内存消耗,因此以这种方式推翻服务器非常容易(除非您有其他断路器)。

  2. 由于 Executor 前面有 aSynchronousQueue这意味着在任务提供者和线程池之间存在直接交接,因此无界问题更加严重。如果所有现有线程都忙,则每个新任务都会创建一个新线程。这对于服务器代码来说通常是一个糟糕的策略。当 CPU 饱和时,现有任务需要更长的时间才能完成。然而,更多的任务被提交,更多的线程被创建,所以任务需要越来越长的时间来完成。当CPU饱和时,更多的线程绝对不是服务器所需要的。

Here are my recommendations:

以下是我的建议:

Use a fixed-size thread pool Executors.newFixedThreadPoolor a ThreadPoolExecutor.with a set maximum number of threads;

使用固定大小的线程池Executors.newFixedThreadPoolThreadPoolExecutor。具有设定的最大线程数;

回答by krmanish007

If you see the code in the grepcode, you will see, they are calling ThreadPoolExecutor.internally and setting their properties. You can create your one to have a better control of your requirement.

如果您看到 grepcode 中的代码,您会看到,它们正在调用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>());
}

回答by Ravindra babu

If you are not worried about an unbounded queue of Callable/Runnabletasks, you can use one of them. As suggested by bruno, I too prefer newFixedThreadPoolto newCachedThreadPoolover these two.

如果您不担心Callable/Runnable任务的无限队列,您可以使用其中之一。正如布鲁诺的建议,我也更喜欢newFixedThreadPoolnewCachedThreadPool了这两个。

But ThreadPoolExecutorprovides more flexible features compared to either newFixedThreadPoolor newCachedThreadPool

ThreadPoolExecutor的相比,提供了更灵活的功能无论是newFixedThreadPoolnewCachedThreadPool

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, 
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)

Advantages:

好处:

  1. You have full control of BlockingQueuesize. It's not un-bounded, unlike the earlier two options. I won't get an out of memory error due to a huge pile-up of pending Callable/Runnable tasks when there is unexpected turbulence in the system.

  2. You can implement custom Rejection handlingpolicy OR use one of the policies:

    1. In the default ThreadPoolExecutor.AbortPolicy, the handler throws a runtime RejectedExecutionException upon rejection.

    2. In ThreadPoolExecutor.CallerRunsPolicy, the thread that invokes execute itself runs the task. This provides a simple feedback control mechanism that will slow down the rate that new tasks are submitted.

    3. In ThreadPoolExecutor.DiscardPolicy, a task that cannot be executed is simply dropped.

    4. In ThreadPoolExecutor.DiscardOldestPolicy, if the executor is not shut down, the task at the head of the work queue is dropped, and then execution is retried (which can fail again, causing this to be repeated.)

  3. You can implement a custom Thread factory for the below use cases:

    1. To set a more descriptive thread name
    2. To set thread daemon status
    3. To set thread priority
  1. 您可以完全控制BlockingQueue 的大小。与前两个选项不同,它不是无限的。当系统中出现意外的动荡时,我不会因为大量未决的 Callable/Runnable 任务而出现内存不足错误。

  2. 您可以实施自定义拒绝处理策略或使用以下策略之一:

    1. 默认情况下ThreadPoolExecutor.AbortPolicy,处理程序在拒绝时抛出运行时 RejectedExecutionException。

    2. 在 中ThreadPoolExecutor.CallerRunsPolicy,调用执行自身的线程运行任务。这提供了一个简单的反馈控制机制,可以减慢提交新任务的速度。

    3. 在 中ThreadPoolExecutor.DiscardPolicy,无法执行的任务被简单地丢弃。

    4. 在 中ThreadPoolExecutor.DiscardOldestPolicy,如果 executor 没有关闭,则工作队列头部的任务被丢弃,然后重试执行(这可能会再次失败,导致重复。)

  3. 您可以为以下用例实现自定义线程工厂:

    1. 设置更具描述性的线程名称
    2. 设置线程守护进程状态
    3. 设置线程优先级

回答by Louis F.

Just to complete the other answers, I would like to quote Effective Java, 2nd Edition, by Joshua Bloch, chapter 10, Item 68 :

为了完成其他答案,我想引用 Joshua Bloch 撰写的 Effective Java,第 2 版,第 10 章,第 68 项:

"Choosing the executor service for a particular application can be tricky. If you're writing a small program, or a lightly loaded server, using Executors.new- CachedThreadPool isgenerally a good choice, as it demands no configuration and generally “does the right thing.” But a cached thread pool is not a good choicefor a heavily loaded production server!

In a cached thread pool, submitted tasks are not queuedbut immediately handed off to a thread for execution. If no threads are available, a new one is created. If a server is so heavily loaded that all of its CPUs are fully utilized, and more tasks arrive, more threads will be created, which will only make matters worse.

Therefore, in a heavily loaded production server, you are much better off using Executors.newFixedThreadPool, which gives you a pool with a fixed number of threads, or using the ThreadPoolExecutor class directly, for maximum control."

“为特定应用程序选择执行器服务可能会很棘手。如果您正在编写一个小程序,或者一个轻负载的服务器,使用Executors.new-CachedThreadPool通常是一个不错的选择,因为它不需要配置并且通常“正确的事。” 但是缓存线程池对于负载很重的生产服务器来说并不是一个好的选择

缓存线程池中提交的任务不会排队,而是立即移交给线程执行。如果没有可用线程,则创建一个新线程。如果服务器负载如此之重,以至于其所有 CPU 都被充分利用,并且有更多任务到达,则会创建更多线程,这只会使情况变得更糟。

因此,在负载很重的生产服务器中,您最好使用Executors.newFixedThreadPool,它为您提供一个具有固定线程数的池,或者直接使用 ThreadPoolExecutor 类以获得最大控制。

回答by Mike Lin

I do some quick tests and have the following findings:

我做了一些快速测试,并有以下发现:

1) if using SynchronousQueue:

1) 如果使用 SynchronousQueue:

After the threads reach the maximum size, any new work will be rejected with the exception like below.

在线程达到最大大小后,任何新工作都将被拒绝,但如下所示。

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@3fee733d rejected from java.util.concurrent.ThreadPoolExecutor@5acf9800[Running, pool size = 3, active threads = 3, queued tasks = 0, completed tasks = 0]

at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)

线程“main”中的异常 java.util.concurrent.RejectedExecutionException:任务 java.util.concurrent.FutureTask@3fee733d 从 java.util.concurrent.ThreadPoolExecutor@5acf9800[Running, pool size = 3, active threads = 3, queued tasks 被拒绝= 0,已完成的任务 = 0]

在 java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)

2) if using LinkedBlockingQueue:

2) 如果使用 LinkedBlockingQueue:

The threads never increase from minimum size to maximum size, meaning the thread pool is fixed size as the minimum size.

线程永远不会从最小大小增加到最大大小,这意味着线程池的大小固定为最小大小。

回答by Ali Dehghani

The ThreadPoolExecutorclass is the base implementation for the executors that are returned from many of the Executorsfactory methods. So let's approach Fixedand Cachedthread pools from ThreadPoolExecutor's perspective.

ThreadPoolExecutor类是由许多的返回执行人基实现Executors工厂方法。因此,让我们从的角度来处理FixedCached线程池ThreadPoolExecutor

ThreadPoolExecutor

线程池执行器

The main constructorof this class looks like this:

此类的主要构造函数如下所示:

public ThreadPoolExecutor(
                  int corePoolSize,
                  int maximumPoolSize,
                  long keepAliveTime,
                  TimeUnit unit,
                  BlockingQueue<Runnable> workQueue,
                  ThreadFactory threadFactory,
                  RejectedExecutionHandler handler
)

Core Pool Size

核心池大小

The corePoolSizedetermines the minimum size of the target thread pool.The implementation would maintain a pool of that size even if there are no tasks to execute.

corePoolSize确定目标线程池的最小大小。即使没有要执行的任务,该实现也会维护一个该大小的池。

Maximum Pool Size

最大池大小

The maximumPoolSizeis the maximum number of threads that can be active at once.

maximumPoolSize是,可以一次活动线程的最大数目。

After the thread pool grows and becomes bigger than the corePoolSizethreshold, the executor can terminate idle threads and reach to the corePoolSizeagain. If allowCoreThreadTimeOutis true, then the executor can even terminate core pool threads if they were idle more than keepAliveTimethreshold.

当线程池增长并大于corePoolSize阈值后,执行器可以终止空闲线程并corePoolSize再次到达。如果allowCoreThreadTimeOut为真,那么如果核心池线程空闲超过keepAliveTime阈值,则执行器甚至可以终止它们。

So the bottom line is if threads remain idle more than keepAliveTimethreshold, they may get terminated since there is no demand for them.

所以底线是如果线程保持空闲超过keepAliveTime阈值,它们可能会被终止,因为对它们没有需求。

Queuing

排队

What happens when a new task comes in and all core threads are occupied?The new tasks will be queued inside that BlockingQueue<Runnable>instance. When a thread becomes free, one of those queued tasks can be processed.

当一个新任务进来并且所有核心线程都被占用时会发生什么?新任务将在该BlockingQueue<Runnable>实例内排队。当一个线程空闲时,可以处理这些排队任务之一。

There are different implementations of the BlockingQueueinterface in Java, so we can implement different queuing approaches like:

BlockingQueueJava 中有不同的接口实现,因此我们可以实现不同的排队方法,例如:

  1. Bounded Queue: New tasks would be queued inside a bounded task queue.

  2. Unbounded Queue: New tasks would be queued inside an unbounded task queue. So this queue can grow as much as the heap size allows.

  3. Synchronous Handoff: We can also use the SynchronousQueueto queue the new tasks. In that case, when queuing a new task, another thread must already be waiting for that task.

  1. 有界队列:新任务将在有界任务队列中排队。

  2. 无界队列:新任务将在无界任务队列中排队。所以这个队列可以在堆大小允许的范围内增长。

  3. 同步切换:我们也可以使用SynchronousQueue来排队新任务。在这种情况下,当排队一个新任务时,另一个线程必须已经在等待该任务。

Work Submission

作品提交

Here's how the ThreadPoolExecutorexecutes a new task:

以下是ThreadPoolExecutor执行新任务的方式:

  1. If fewer than corePoolSizethreads are running, tries to start a new thread with the given task as its first job.
  2. Otherwise, it tries to enqueue the new task using the BlockingQueue#offermethod. The offermethod won't block if the queue is full and immediately returns false.
  3. If it fails to queue the new task (i.e. offerreturns false), then it tries to add a new thread to the thread pool with this task as its first job.
  4. If it fails to add the new thread, then the executor is either shut down or saturated. Either way, the new task would be rejected using the provided RejectedExecutionHandler.
  1. 如果少于corePoolSize线程正在运行,则尝试以给定任务作为其第一个作业启动一个新线程。
  2. 否则,它会尝试使用该BlockingQueue#offer方法将新任务加入队列 。offer如果队列已满,该方法不会阻塞并立即返回false
  3. 如果它无法将新任务排队(即offer返回false),那么它会尝试将此任务作为其第一个作业添加到线程池中的新线程。
  4. 如果添加新线程失败,则执行器要么关闭,要么饱和。无论哪种方式,新任务都将使用提供的RejectedExecutionHandler.

The main difference between the fixed and cached thread pools boils down to these three factors:

固定线程池和缓存线程池之间的主要区别归结为以下三个因素:

  1. Core Pool Size
  2. Maximum Pool Size
  3. Queuing
  1. 核心池大小
  2. 最大池大小
  3. 排队
+-----------+-----------+-------------------+---------------------------------+
| Pool Type | Core Size |    Maximum Size   |         Queuing Strategy        |
+-----------+-----------+-------------------+---------------------------------+
|   Fixed   | n (fixed) |     n (fixed)     | Unbounded `LinkedBlockingQueue` |
+-----------+-----------+-------------------+---------------------------------+
|   Cached  |     0     | Integer.MAX_VALUE |        `SynchronousQueue`       |
+-----------+-----------+-------------------+---------------------------------+



Fixed Thread Pool

固定线程池



这是Excutors.newFixedThreadPool(n)Excutors.newFixedThreadPool(n)工作原理:

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

As you can see:

如你看到的:

  • The thread pool size is fixed.
  • If there is high demand, it won't grow.
  • If threads are idle for quite some time, it won't shrink.
  • Suppose all those threads are occupied with some long-running tasks and the arrival rate is still pretty high. Since the executor is using an unbounded queue, it may consume a huge part of the heap. Being unfortunate enough, we may experience an OutOfMemoryError.
  • 线程池大小是固定的。
  • 如果需求量大,就不会增长。
  • 如果线程空闲很长时间,它不会收缩。
  • 假设所有这些线程都被一些长时间运行的任务占用,并且到达率仍然很高。由于 executor 使用的是无界队列,它可能会消耗大量的堆。不幸的是,我们可能会遇到OutOfMemoryError.

When should I use one or the other? Which strategy is better in terms of resource utilization?

我什么时候应该使用其中一种?哪种策略在资源利用方面更好?

A fixed-size thread pool seems to be a good candidate when we're going to limit the number of concurrent tasks for resource management purposes.

当我们出于资源管理目的而限制并发任务的数量时,固定大小的线程池似乎是一个不错的选择

For example, if we're going to use an executor to handle web server requests, a fixed executor can handle the request bursts more reasonably.

例如,如果我们要使用一个 executor 来处理 web 服务器请求,固定的 executor 可以更合理地处理请求突发。

For even better resource management, it's highly recommended to create a custom ThreadPoolExecutorwith a bounded BlockingQueue<T>implementation coupled with reasonable RejectedExecutionHandler.

为了更好的资源管理,强烈建议创建一个ThreadPoolExecutor有界BlockingQueue<T>实现的自定义和合理的RejectedExecutionHandler.



Cached Thread Pool

缓存线程池



Here's how the Executors.newCachedThreadPool()works:

这是Executors.newCachedThreadPool()工作原理:

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

As you can see:

如你看到的:

  • The thread pool can grow from zero threads to Integer.MAX_VALUE. Practically, the thread pool is unbounded.
  • If any thread is idle for more than 1 minute, it may get terminated. So the pool can shrink if threads remain too much idle.
  • If all allocated threads are occupied while a new task comes in, then it creates a new thread, as offering a new task to a SynchronousQueuealways fails when there is no one on the other end to accept it!
  • 线程池可以从零个线程增长到Integer.MAX_VALUE. 实际上,线程池是无界的。
  • 如果任何线程空闲超过 1 分钟,它可能会被终止。因此,如果线程保持过多空闲状态,则池会缩小。
  • 如果在新任务进入时所有分配的线程都被占用,那么它会创建一个新线程,因为SynchronousQueue当另一端没有人接受时,向 a 提供新任务总是失败!

When should I use one or the other? Which strategy is better in terms of resource utilization?

我什么时候应该使用其中一种?哪种策略在资源利用方面更好?

Use it when you have a lot of predictable short-running tasks.

当您有很多可预测的短期运行任务时使用它。