Java Thread Pool – ThreadPoolExecutor Example

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.

Thread Pool
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.

  1. 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);
  2. 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();
  3. 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);
  4. 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();
  5. 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.

  1. ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) – Creates and executes a task that becomes enabled after the given delay.
  2. ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) – Creates and executes a ScheduledFuture that becomes enabled after the given delay.
  3. ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long delay, TimeUnit unit) – Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given delay period. If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute.
  4. ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) – Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given delay period. No matter how much time a long running task takes, there will be a fixed delay 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

  1. The ThreadPoolExecutor class has four different constructors but, due to their complexity, the Java concurrency API provides the Executors class to construct executors and other related objects. Although we can create ThreadPoolExecutor directly using one of its constructors, it’s recommended to use the Executors class.
  2. 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.
  3. 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.
  4. To indicate to the executor that you want to finish it, you can use the shutdown() method of the ThreadPoolExecutor class. When the executor finishes the execution of all pending tasks, it finishes its execution. After you call the shutdown() method, if you try to send another task to the executor, it will be rejected and the executor will throw a RejectedExecutionException exception.
  5. The ThreadPoolExecutor class provides a lot of methods to obtain information about its status. We used in the example the getPoolSize(), getActiveCount(), and getCompletedTaskCount() 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 the getLargestPoolSize() method that returns the maximum number of threads that has been in the pool at a time.
  6. 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() or shutdownNow() 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:

Thread Pool – Wikipedia

Was this post helpful?

Join 7000+ Fellow Programmers

Subscribe to get new post notifications, industry updates, best practices, and much more. Directly into your inbox, for free.

19 thoughts on “Java Thread Pool – ThreadPoolExecutor Example”

  1. 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!

  2. 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.

  3. 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

  4. 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!

  5. 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

  6. 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 … [:-)]

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

Comments are closed.

HowToDoInJava

A blog about Java and its related technologies, the best practices, algorithms, interview questions, scripting languages, and Python.