Thread pool in Java with Example

A thread pool in Java is a group of pre-instantiated, idle threads that stand ready to be given work. This is an efficient way to manage threads because it eliminates the overhead of thread creation and destruction. Java provides thread pool management through the java.util.concurrent package, particularly with ExecutorService and its implementations.

Why Use a Thread Pool?

  • Resource Management: Thread creation has overhead. Too many threads can lead to reduced performance, as each thread consumes memory and CPU cycles.
  • Work Queue Management: Allows you to efficiently manage a queue of tasks.
  • Performance Improvement: Reusing existing threads can significantly improve performance, especially for applications that involve a large number of short-lived tasks.

Example of Using a Thread Pool in Java

Here’s a simple example demonstrating how to use a thread pool using ExecutorService:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {

    public static void main(String[] args) {
        // Create a thread pool with a fixed number of threads
        ExecutorService executor = Executors.newFixedThreadPool(5);

        // Submit 10 tasks to the thread pool
        for (int i = 0; i < 10; i++) {
            Runnable task = new Task(i);
            executor.execute(task);
        }

        // Shutdown the executor
        executor.shutdown();
    }
}

class Task implements Runnable {
    private final int taskId;

    public Task(int id) {
        this.taskId = id;
    }

    @Override
    public void run() {
        System.out.println("Task ID : " + this.taskId + " performed by " 
                           + Thread.currentThread().getName());
    }
}

In this example:

  • The ExecutorService is created using Executors.newFixedThreadPool(5), which means the thread pool contains 5 threads.
  • We then submit 10 tasks to the executor. These tasks are instances of Task, which implements Runnable.
  • Each task simply prints its ID along with the name of the thread executing it.
  • Finally, we shut down the executor. It’s important to shut down the executor when it’s no longer needed to free up system resources.

Notes:

  1. Shutdown of ExecutorService: Always remember to shut down the executor once you are done with it. Failing to do this can lead to your application not terminating because the non-daemon threads are still alive.
  2. Types of Executors: There are different types of executors provided in the java.util.concurrent.Executors class for different use cases, like newCachedThreadPool() and newSingleThreadExecutor().
  3. Task Submission: You can submit tasks for execution in the thread pool using execute(Runnable) for tasks that don’t return a result, or submit(Callable) for tasks that return a result.
  4. Handling Rejected Tasks: When the executor is shut down, it will reject new tasks. You can customize the rejection policy as per your requirements.

How to create thread pool in Java

In Java, there are several common ways to create a thread pool, mainly using different types of executors provided by the java.util.concurrent.Executors class. Each type of executor is designed for different kinds of tasks and workloads. Here are the primary ones:

  1. Fixed Thread Pool
  • Created using Executors.newFixedThreadPool(int nThreads).
  • Consists of a fixed number of threads.
  • If additional tasks are submitted when all threads are active, they will wait in the queue until a thread is available.
  1. Cached Thread Pool
  • Created using Executors.newCachedThreadPool().
  • Creates new threads as needed, but will reuse previously constructed threads when they are available.
  • Ideal for applications that have many short-lived asynchronous tasks.
  1. Single Thread Executor
  • Created using Executors.newSingleThreadExecutor().
  • Contains only one thread.
  • All tasks are executed sequentially (one at a time).
  1. Scheduled Thread Pool
  • Created using Executors.newScheduledThreadPool(int corePoolSize).
  • Can schedule commands to run after a given delay or to execute periodically.
  • Useful for tasks that need to be executed repeatedly or with a delay.
  1. Work Stealing Pool
  • Created using Executors.newWorkStealingPool(int parallelism) in Java 8 and later.
  • A pool of threads appropriate for applications that involve many short-lived tasks.
  • Uses a work-stealing algorithm to dynamically redistribute work among available threads.
  1. Custom Thread Pool
  • Created by directly using the ThreadPoolExecutor constructor.
  • Provides the most flexibility as you can specify the core and maximum pool sizes, keep-alive time, work queue, and the thread factory.

Example of Creating a Custom Thread Pool:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class CustomThreadPoolExample {
    public static void main(String[] args) {
        int corePoolSize = 5;
        int maxPoolSize = 10;
        long keepAliveTime = 5000;

        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            corePoolSize,
            maxPoolSize,
            keepAliveTime,
            TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<>(100)
        );

        // Add tasks to the executor here...

        executor.shutdown();
    }
}

Choosing the Right Type:

  • For Many Short-Lived Tasks: Use a cached thread pool or a work-stealing pool.
  • For Fixed-Size Parallelism: Use a fixed thread pool.
  • For Single-Threaded Execution: Use a single thread executor.
  • For Scheduling Tasks: Use a scheduled thread pool.
  • For Specific Requirements: Use a custom thread pool.

Each type of thread pool has its use case depending on the nature of the tasks you need to execute. For most general purposes, the pre-defined executors provided by the Executors class will suffice. However, if you need more control over the thread pool’s characteristics, creating a custom thread pool using ThreadPoolExecutor is the way to go.

Using a thread pool is a common practice for managing resources efficiently in multi-threaded Java applications, especially when dealing with a large number of short-lived asynchronous tasks.

Other multithreading tutorials.

How to create a thread in java.
Thread life cycle in java.
Thread class constructors and methods in java.
How run() method get called using start() method.
The Runnable interface in java.
Thread Priority in Java with example.
Daemon thread in java with example.
Thread yield() method in java.
Thread sleep() method in java.
Thread join() method in java.
wait(),notify() and notifyAll() in java.
Difference between sleep() and wait() method in java.
Synchronization in java with example.