Why you need thread pool in Java? Answer is usually when you develop a simple, concurrent application in Java, you create some Runnable
objects and then create the corresponding Thread
objects to execute them. Creating a thread in Java is an expensive operation. And if you start creating new thread instance everytime to execute a task, application performance will degrade surely.
Table of Contents 1. How thread pool works in java 2. ThreadPoolExecutor 3. How to create ThreadPoolExecutor 4. ThreadPoolExecutor Example 5. ScheduledThreadPoolExecutor 6. Custom thread pool implementation in java 7. Summary
1. How thread pool works in java
A thread pool is a collection of pre-initialized threads. Generally, the size of the collection is fixed, but it is not mandatory. It facilitates the execution of N number of tasks using the same threads. If there are more tasks than threads, then tasks need to wait in a queue like structure (FIFO – First in first out).
When any thread completes its execution, it can pickup a new task from the queue and execute it. When all tasks are completed the threads remain active and wait for more tasks in the thread pool.

A watcher keep watching queue (usually BlockingQueue) for any new tasks. As soon as tasks come, threads again start picking up tasks and execute them.
2. ThreadPoolExecutor
Since Java 5, the Java concurrency API provides a mechanism Executor framework. This is around the Executor
interface, its sub-interface ExecutorService
, and the ThreadPoolExecutor
class that implements both interfaces.
ThreadPoolExecutor
separates the task creation and its execution. With ThreadPoolExecutor
, you only have to implement the Runnable
objects and send them to the executor. It is responsible for their execution, instantiation, and running with necessary threads.
It goes beyond that and improves performance using a pool of threads. When you send a task to the executor, it tries to use a pooled thread for the execution of this task, to avoid continuous spawning of threads.
3. How to create ThreadPoolExecutor
We can create following 5 types of thread pool executors with pre-built methods in java.util.concurrent.Executors
interface.
- Fixed thread pool executor – Creates a thread pool that reuses a fixed number of threads to execute any number of tasks. If additional tasks are submitted when all threads are active, they will wait in the queue until a thread is available. It is the best fit for most off the real-life use-cases.
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
- Cached thread pool executor – Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available. DO NOT use this thread pool if tasks are long-running. It can bring down the system if the number of threads goes beyond what the system can handle.
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
- Scheduled thread pool executor – Creates a thread pool that can schedule commands to run after a given delay, or to execute periodically.
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newScheduledThreadPool(10);
- Single thread pool executor – Creates single thread to execute all tasks. Use it when you have only one task to execute.
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newSingleThreadExecutor();
- Work stealing thread pool executor – Creates a thread pool that maintains enough threads to support the given parallelism level. Here parallelism level means the maximum number of threads which will be used to execute a given task, at a single point of time, in multi-processor machines.
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newWorkStealingPool(4);
4. ThreadPoolExecutor Example
4.1. Create Task
Let’s create a task which will take random time to complete it, everytime.
package com.howtodoinjava.threads; import java.util.concurrent.TimeUnit; public class Task implements Runnable { private String name; public Task(String name) { this.name = name; } public String getName() { return name; } public void run() { try { Long duration = (long) (Math.random() * 10); System.out.println("Executing : " + name); TimeUnit.SECONDS.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } } }
4.2. Execute tasks with thread pool executor
The given program creates 5 tasks and submit to the executor queue. The executor uses two threads to execute all tasks.
package com.howtodoinjava.threads; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; public class ThreadPoolExample { public static void main(String[] args) { ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2); for (int i = 1; i <= 5; i++) { Task task = new Task("Task " + i); System.out.println("Created : " + task.getName()); executor.execute(task); } executor.shutdown(); } }
Program output:
Created : Task 1 Created : Task 2 Created : Task 3 Created : Task 4 Created : Task 5 Executing : Task 1 Executing : Task 2 Executing : Task 3 Executing : Task 4 Executing : Task 5
5. ScheduledThreadPoolExecutor
Fixed thread pools or cached thread pools are good when you have to execute one unique task only once. When you need to execute a task, repeatedly N times, either N fixed number of times or infinitely after fixed delay, you should be using ScheduledThreadPoolExecutor.
ScheduledThreadPoolExecutor
provides 4 methods which provide different capabilities to execute the tasks in repeated manner.
ScheduledFuture> schedule(Runnable command, long delay, TimeUnit unit)
– Creates and executes a task that becomes enabled after the given delay.ScheduledFuture> schedule(Callable
– Creates and executes acallable, long delay, TimeUnit unit) ScheduledFuture
that becomes enabled after the given delay.ScheduledFuture> scheduleAtFixedRate(Runnable command, long initialDelay, long delay, TimeUnit unit)
– Creates and executes a periodic action that becomes enabled first after the giveninitial
delay, and subsequently with the givendelay
period. If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute.ScheduledFuture> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
– Creates and executes a periodic action that becomes enabled first after the giveninitial
delay, and subsequently with the givendelay
period. No matter how much time a long running task takes, there will be a fixeddelay
time gap between two executions.
5.1. ScheduledThreadPoolExecutor Example
package com.howtodoinjava.threads; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class ScheduledThreadPoolExecutorExample { public static void main(String[] args) { ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(2); Task task = new Task("Repeat Task"); System.out.println("Created : " + task.getName()); executor.scheduleWithFixedDelay(task, 2, 2, TimeUnit.SECONDS); } } class Task implements Runnable { private String name; public Task(String name) { this.name = name; } public String getName() { return name; } public void run() { System.out.println("Executing : " + name + ", Current Seconds : " + new Date().getSeconds()); } }
Program output:
Created : Repeat Task Executing : Repeat Task, Current Seconds : 36 Executing : Repeat Task, Current Seconds : 38 Executing : Repeat Task, Current Seconds : 41 Executing : Repeat Task, Current Seconds : 43 Executing : Repeat Task, Current Seconds : 45 Executing : Repeat Task, Current Seconds : 47
6. Custom thread pool implementation in java
Though Java has very robust thread pool functionality through Executor framework. And you should not be creating your own custom thread pool without executor. I will strongly discourage any such attempt. Yet if you would like to create it for your own learning, the given below is such thread pool implementation in Java.
package com.howtodoinjava.threads; import java.util.concurrent.LinkedBlockingQueue; @SuppressWarnings("unused") public class CustomThreadPool { //Thread pool size private final int poolSize; //Internally pool is an array private final WorkerThread[] workers; // FIFO ordering private final LinkedBlockingQueue<Runnable> queue; public CustomThreadPool(int poolSize) { this.poolSize = poolSize; queue = new LinkedBlockingQueue<Runnable>(); workers = new WorkerThread[poolSize]; for (int i = 0; i < poolSize; i++) { workers[i] = new WorkerThread(); workers[i].start(); } } public void execute(Runnable task) { synchronized (queue) { queue.add(task); queue.notify(); } } private class WorkerThread extends Thread { public void run() { Runnable task; while (true) { synchronized (queue) { while (queue.isEmpty()) { try { queue.wait(); } catch (InterruptedException e) { System.out.println("An error occurred while queue is waiting: " + e.getMessage()); } } task = (Runnable) queue.poll(); } try { task.run(); } catch (RuntimeException e) { System.out.println("Thread pool is interrupted due to an issue: " + e.getMessage()); } } } } public void shutdown() { System.out.println("Shutting down thread pool"); for (int i = 0; i < poolSize; i++) { workers[i] = null; } } }
Execute same task which we executed with ThreadPoolExecutor
.
package com.howtodoinjava.threads; public class CustomThreadPoolExample { public static void main(String[] args) { CustomThreadPool customThreadPool = new CustomThreadPool(2); for (int i = 1; i <= 5; i++) { Task task = new Task("Task " + i); System.out.println("Created : " + task.getName()); customThreadPool.execute(task); } } }
Program output:
Created : Task 1 Created : Task 2 Created : Task 3 Created : Task 4 Created : Task 5 Executing : Task 1 Executing : Task 2 Executing : Task 3 Executing : Task 4 Executing : Task 5
Above is a very raw thread pool implementation with scope of lots of improvements. But still, rather than perfecting above code, focus on learning Java executor framework.
Also note that incorrect pooling or queue handling can result in deadlocks or resource thrashing as well. You can certainly avoid these problems with the Executor framework which is well tested by the Java community.
7. Summary
- The
ThreadPoolExecutor
class has four different constructors but, due to their complexity, the Java concurrency API provides theExecutors
class to construct executors and other related objects. Although we can createThreadPoolExecutor
directly using one of its constructors, it’s recommended to use theExecutors
class. - The cached thread pool, we have created above, creates new threads if needed to execute the new tasks, and reuses the existing ones if they have finished the execution of the task they were running, which are now available. The cached thread pool has, however, a disadvantage of constant lying threads for new tasks, so if you send too many tasks to this executor, you can overload the system. This can be overcome using fixed thread pool, which we will learn in next tutorial.
- One critical aspect of the
ThreadPoolExecutor
class, and of the executors in general, is that you have to end it explicitly. If you don’t do this, the executor will continue its execution and the program won’t end. If the executor doesn’t have tasks to execute, it continues waiting for new tasks and it doesn’t end its execution. A Java application won’t end until all its non-daemon threads finish their execution, so, if you don’t terminate the executor, your application will never end. - To indicate to the executor that you want to finish it, you can use the
shutdown()
method of theThreadPoolExecutor
class. When the executor finishes the execution of all pending tasks, it finishes its execution. After you call theshutdown()
method, if you try to send another task to the executor, it will be rejected and the executor will throw aRejectedExecutionException
exception. - The
ThreadPoolExecutor
class provides a lot of methods to obtain information about its status. We used in the example thegetPoolSize()
,getActiveCount()
, andgetCompletedTaskCount()
methods to obtain information about the size of the pool, the number of threads, and the number of completed tasks of the executor. You can also use thegetLargestPoolSize()
method that returns the maximum number of threads that has been in the pool at a time. - The
ThreadPoolExecutor
class also provides other methods related with the finalization of the executor. These methods are:- shutdownNow(): This method shut downs the executor immediately. It doesn’t execute the pending tasks. It returns a list with all these pending tasks. The tasks that are running when you call this method continue with their execution, but the method doesn’t wait for their finalization.
- isTerminated(): This method returns true if you have called the
shutdown()
orshutdownNow()
methods and the executor finishes the process of shutting it down. - isShutdown(): This method returns true if you have called the
shutdown()
method of the executor. - awaitTermination(long timeout,TimeUnitunit): This method blocks the calling thread until the tasks of the executor have ended or the timeout occurs. The
TimeUnit
class is an enumeration with the following constants:DAYS
,HOURS
,MICROSECONDS
etc.
Happy Learning !!
References:
Thanks for the examples! Note that the shutdown() in the last example does not actually stop the run loop in the worker threads. One way to do that would be to send a special “poison pill” message to the queue.
Hi Lokesh, Class WorkerThread instead of extending thread class, I used runnable interface.
The program is not working as expected. It is getting into deadlock here:
synchronized (queue) {
while(queue.isEmpty()) {
The program runs fine if I am extending Thread class.
Could you please throw some light on why it is not working for Runnable interface.
Thanks You!
Thanks for this valuable articles.
If there are more number of tasks to be executed then better to use Executors.newFixedThreadPool() method that creates a thread pool that reuses a fixed number of threads.
I want to use ThreadPoolExecution for parallel index creation in lucene with a return type in the runnable class. I dont see run(0 supports this. Could you help how can we do thath
use callable instead of runnable if you want to get output from execution of thread.
Very nice article and i have found it so helpful and informative. This simple class allows you to create Thread Pools and thread factories.With an Executor Service, you can publish process that will be finished later on, thanks a lot for sharing this valuable article!
Hi
When i am trying to call execute method from executor reference variable , compiler gives me an error.Can you tell me what is the reason
Please check the class or interface you are using because Executors is class and Executor is interface
Hi Lokesh, I have 2 questions
1. What is the time lag in creating a pool of 10 threads ?
2. I have an RESTful application that will be deployed on a JBOSS container.
Once I receive a request , my intention is to perform 6 concurrent operations. Can I use ThreadPoolExecutor to do so ?
Considering that I would be spawning 6 new threads from the request thread spawned by JBOSS , for handling the REST request.
1) Lag depends on application runtime load – but normally it’s always enough to avoid it.
2) Yes, executor is always a good idea in such scenario.
Read point 2 in important points regarding creation/reuse of new thread instances.
Lokesh,
But if we have to shutdown Executor upon serving each RESTful request, don’t we have to spawn off new 6 new threads everytime a new RESTful request comes in? If so, doesn’t that beat the purpose of pooling?
Thanks in advance.
Chris
Cris, if one need to shutdown executor after each request complete, then, perhaps better, he should should not use it. Threads and executors are expensive operations and should be designed with care.
And sometimes I feel lost, when we talk about things with proper case study or any specific scenario … [:-)]
Really nice,Thanks for ur tut…..I understand about fully threadpoolexecuter….
Hello,
If I have 2 million records to process. How would I control the amount of records in the queue so that it doesn’t use up all my system resources? Also, we are creating a new instance of Task for every record to be processed, wouldn’t that defeat the purpose of threadpooling? How can I reuse the created instance of Task in the Threadpool?
for (int i = 0; i <= 5; i++)
{
Task task = new Task("Task " + i);
System.out.println("A new task has been added : " + task.getName());
executor.execute(task);
}
In that case you need to write the code to get the resource inside run method, after checking if all resources are not consumed. In this way you will have fixed number of threads.
Thanks for such a nice explanation.It really helped.
Nice explaination ,
I have one doubt.
how can i persist a job on web application reload or web server restarts
Sure. For that you need to use database to store job informations.
This is really helpful to understand ThreadPoolExecutor functionality. Thank you for your nice explanation.