Java Thread Pool Example Tutorial

Introduction

A thread pool in Java is a managed collection of worker threads that are reused to perform multiple tasks. This mechanism helps improve performance by reducing the overhead associated with creating and destroying threads. The java.util.concurrent package provides several classes and interfaces to work with thread pools, such as Executor, ExecutorService, Executors, and ThreadPoolExecutor.

Table of Contents

  1. What is a Thread Pool?
  2. Benefits of Using a Thread Pool
  3. Creating a Thread Pool
  4. Submitting Tasks to a Thread Pool
  5. Shutting Down a Thread Pool
  6. Example: Fixed Thread Pool
  7. Example: Cached Thread Pool
  8. Example: Single Thread Executor
  9. Example: Scheduled Thread Pool
  10. Conclusion

1. What is a Thread Pool?

A thread pool manages a pool of worker threads, which are reused to execute tasks. When a task is submitted to a thread pool, it assigns a thread to execute the task. Once the task is completed, the thread returns to the pool and is ready to execute another task.

2. Benefits of Using a Thread Pool

  • Resource Management: Thread pools manage a fixed number of threads, preventing resource exhaustion.
  • Performance Improvement: Reusing threads reduces the overhead of creating and destroying threads.
  • Task Management: Thread pools provide mechanisms to manage and schedule tasks efficiently.

3. Creating a Thread Pool

The Executors class provides factory methods to create different types of thread pools:

  • newFixedThreadPool(int nThreads): Creates a thread pool with a fixed number of threads.
  • newCachedThreadPool(): Creates a thread pool that creates new threads as needed but reuses previously constructed threads when they are available.
  • newSingleThreadExecutor(): Creates a thread pool with a single worker thread.
  • newScheduledThreadPool(int corePoolSize): Creates a thread pool that can schedule tasks to run after a delay or periodically.

4. Submitting Tasks to a Thread Pool

You can submit tasks to a thread pool using the submit() or execute() methods of ExecutorService.

Example:

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

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);

        for (int i = 1; i <= 5; i++) {
            Runnable worker = new WorkerThread("" + i);
            executor.execute(worker);
        }

        executor.shutdown();
        while (!executor.isTerminated()) {
        }

        System.out.println("Finished all threads");
    }
}

class WorkerThread implements Runnable {
    private String command;

    public WorkerThread(String command) {
        this.command = command;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " Start. Command = " + command);
        processCommand();
        System.out.println(Thread.currentThread().getName() + " End.");
    }

    private void processCommand() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String toString() {
        return this.command;
    }
}

Output:

pool-1-thread-1 Start. Command = 1
pool-1-thread-2 Start. Command = 2
pool-1-thread-1 End.
pool-1-thread-1 Start. Command = 3
pool-1-thread-2 End.
pool-1-thread-2 Start. Command = 4
pool-1-thread-1 End.
pool-1-thread-1 Start. Command = 5
pool-1-thread-2 End.
pool-1-thread-1 End.
Finished all threads

5. Shutting Down a Thread Pool

It is important to shut down the thread pool after use to free up system resources. This can be done using the shutdown() or shutdownNow() methods.

  • shutdown(): Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.
  • shutdownNow(): Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were waiting to be executed.

6. Example: Fixed Thread Pool

A fixed thread pool has a fixed number of threads. If a thread is not available, the task will wait in the queue until a thread becomes available.

Example:

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

public class FixedThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        for (int i = 1; i <= 5; i++) {
            Runnable worker = new WorkerThread("" + i);
            executor.execute(worker);
        }

        executor.shutdown();
        while (!executor.isTerminated()) {
        }

        System.out.println("Finished all threads");
    }
}

Output:

pool-1-thread-1 Start. Command = 1
pool-1-thread-2 Start. Command = 2
pool-1-thread-3 Start. Command = 3
pool-1-thread-1 End.
pool-1-thread-1 Start. Command = 4
pool-1-thread-2 End.
pool-1-thread-2 Start. Command = 5
pool-1-thread-3 End.
pool-1-thread-1 End.
pool-1-thread-2 End.
Finished all threads

7. Example: Cached Thread Pool

A cached thread pool creates new threads as needed but reuses previously constructed threads when they are available. This can be useful for applications that execute many short-lived asynchronous tasks.

Example:

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

public class CachedThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();

        for (int i = 1; i <= 5; i++) {
            Runnable worker = new WorkerThread("" + i);
            executor.execute(worker);
        }

        executor.shutdown();
        while (!executor.isTerminated()) {
        }

        System.out.println("Finished all threads");
    }
}

Output:

pool-1-thread-1 Start. Command = 1
pool-1-thread-2 Start. Command = 2
pool-1-thread-3 Start. Command = 3
pool-1-thread-4 Start. Command = 4
pool-1-thread-5 Start. Command = 5
pool-1-thread-1 End.
pool-1-thread-2 End.
pool-1-thread-3 End.
pool-1-thread-4 End.
pool-1-thread-5 End.
Finished all threads

8. Example: Single Thread Executor

A single thread executor has only one thread. It ensures that tasks are executed sequentially, in the order they are submitted.

Example:

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

public class SingleThreadExecutorExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();

        for (int i = 1; i <= 5; i++) {
            Runnable worker = new WorkerThread("" + i);
            executor.execute(worker);
        }

        executor.shutdown();
        while (!executor.isTerminated()) {
        }

        System.out.println("Finished all threads");
    }
}

Output:

pool-1-thread-1 Start. Command = 1
pool-1-thread-1 End.
pool-1-thread-1 Start. Command = 2
pool-1-thread-1 End.
pool-1-thread-1 Start. Command = 3
pool-1-thread-1 End.
pool-1-thread-1 Start. Command = 4
pool-1-thread-1 End.
pool-1-thread-1 Start. Command = 5
pool-1-thread-1 End.
Finished all threads

9. Example: Scheduled Thread Pool

A scheduled thread pool can schedule tasks to run after a delay or periodically. It is useful for recurring tasks.

Example:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadPoolExample {
    public static void main(String[] args) {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);

        Runnable task1 = new WorkerThread("Task 1");
        Runnable task2 = new WorkerThread("Task 2");

        executor.schedule(task1, 5, TimeUnit.SECONDS);
        executor.schedule(task2, 10, TimeUnit.SECONDS);

        executor.shutdown();
        while (!executor.isTerminated()) {
        }

        System.out.println("Finished all threads");
    }
}

Output:

pool-1-thread-1 Start. Command = Task 1
pool-1-thread-1 End.
pool-1-thread-2 Start. Command = Task 2
pool-1-thread-2 End.
Finished all threads

10. Conclusion

Thread pools in Java provide a powerful way to manage multiple threads and improve the performance of concurrent applications. They help in managing resources efficiently by reusing existing threads and limiting the number of active threads. Understanding how to create and use different types of thread pools can greatly enhance your ability to write robust and efficient Java applications.

Happy coding!

Comments