[java] Executors.newCachedThreadPool() versus Executors.newFixedThreadPool()

newCachedThreadPool() versus newFixedThreadPool()

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

The answer is


I do some quick tests and have the following findings:

1) if using 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)

2) if using LinkedBlockingQueue:

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


The ThreadPoolExecutor class is the base implementation for the executors that are returned from many of the Executors factory methods. So let's approach Fixed and Cached thread pools from ThreadPoolExecutor's perspective.

ThreadPoolExecutor

The main constructor of 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 corePoolSize determines 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.

Maximum Pool Size

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

After the thread pool grows and becomes bigger than the corePoolSize threshold, the executor can terminate idle threads and reach to the corePoolSize again. If allowCoreThreadTimeOut is true, then the executor can even terminate core pool threads if they were idle more than keepAliveTime threshold.

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

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.

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

  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 SynchronousQueue to queue the new tasks. In that case, when queuing a new task, another thread must already be waiting for that task.

Work Submission

Here's how the ThreadPoolExecutor executes a new task:

  1. If fewer than corePoolSize threads 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#offer method. The offer method won't block if the queue is full and immediately returns false.
  3. If it fails to queue the new task (i.e. offer returns 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.

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

+-----------+-----------+-------------------+---------------------------------+
| Pool Type | Core Size |    Maximum Size   |         Queuing Strategy        |
+-----------+-----------+-------------------+---------------------------------+
|   Fixed   | n (fixed) |     n (fixed)     | Unbounded `LinkedBlockingQueue` |
+-----------+-----------+-------------------+---------------------------------+
|   Cached  |     0     | Integer.MAX_VALUE |        `SynchronousQueue`       |
+-----------+-----------+-------------------+---------------------------------+


Fixed Thread Pool


Here's how the Excutors.newFixedThreadPool(n) works:

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.

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.

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


Cached Thread Pool


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

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 SynchronousQueue always fails when there is no one on the other end to accept it!

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.


If you look at the source code, 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.

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

Just to complete the other answers, I would like to quote Effective Java, 2nd Edition, by Joshua Bloch, chapter 10, Item 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 is generally a good choice, as it demands no configuration and generally “does the right thing.” But a cached thread pool is not a good choice for a heavily loaded production server!

In a cached thread pool, submitted tasks are not queued but 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."


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

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 SynchronousQueue which 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.

Here are my recommendations:

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


If you are not worried about an unbounded queue of Callable/Runnable tasks, you can use one of them. As suggested by bruno, I too prefer newFixedThreadPool to newCachedThreadPool over these two.

But ThreadPoolExecutor provides more flexible features compared to either newFixedThreadPool or newCachedThreadPool

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

Advantages:

  1. You have full control of BlockingQueue size. 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 handling policy 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

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/).


Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to multithreading

How can compare-and-swap be used for a wait-free mutual exclusion for any shared data structure? Waiting until the task finishes What is the difference between Task.Run() and Task.Factory.StartNew() Why is setState in reactjs Async instead of Sync? What exactly is std::atomic? Calling async method on button click WAITING at sun.misc.Unsafe.park(Native Method) How to use background thread in swift? What is the use of static synchronized method in java? Locking pattern for proper use of .NET MemoryCache

Examples related to concurrency

WAITING at sun.misc.Unsafe.park(Native Method) What is the Swift equivalent to Objective-C's "@synchronized"? Custom thread pool in Java 8 parallel stream How to check if another instance of my shell script is running How to use the CancellationToken property? What's the difference between a Future and a Promise? Why use a ReentrantLock if one can use synchronized(this)? NSOperation vs Grand Central Dispatch What's the difference between Thread start() and Runnable run() multiprocessing.Pool: When to use apply, apply_async or map?

Examples related to executorservice

Naming threads and thread-pools of ExecutorService How to get thread id from a thread pool? ExecutorService, how to wait for all tasks to finish ExecutorService that interrupts tasks after a timeout Handling exceptions from Java ExecutorService tasks How to wait for all threads to finish, using ExecutorService? Executors.newCachedThreadPool() versus Executors.newFixedThreadPool() Java Timer vs ExecutorService?

Examples related to threadpoolexecutor

Handling exceptions from Java ExecutorService tasks Executors.newCachedThreadPool() versus Executors.newFixedThreadPool()