Thread Pools

In this section we'll explore thread pools, a method for efficiently managing multiple threads simultaneously.

What are Thread Pools?

Thread pools manage a set number of threads that handle multiple tasks. This is akin to having workers in a factory where each worker completes a task before starting a new one. Thread pools help optimize resource usage and improve performance by reusing threads instead of constantly creating new ones.

Implementing a Processor Class

First, we define a Processor class that implements the Runnable interface:

public class Processor implements Runnable {
    private int id;

    public Processor(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        System.out.println("Starting: " + id);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Completed: " + id);
    }
}

Setting Up the Thread Pool

Next, we set up the thread pool in our main application:

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

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

        for (int i = 0; i < 5; i++) {
            executor.submit(new Processor(i));
        }

        executor.shutdown();
        System.out.println("All tasks submitted.");

        try {
            executor.awaitTermination(1, TimeUnit.DAYS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("All tasks completed.");
    }
}

Explanation

  1. Creating the Thread Pool:
ExecutorService executor = Executors.newFixedThreadPool(2);

This creates a thread pool with two threads.

  1. Submitting Taks
for (int i = 0; i < 5; i++) {
    executor.submit(new Processor(i));
}

We submit five tasks to the thread pool. Each task is handled by a Processor instance.

  1. Shutting Down the Executor:
executor.shutdown();
System.out.println("All tasks submitted.");

This initiates an orderly shutdown where previously submitted tasks are executed, but no new tasks will be accepted.

  1. Awaiting Termination
try {
    executor.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
    e.printStackTrace();
}

This waits for all tasks to complete, with a maximum wait time of one day.

Running the Application

When the application runs:

  • Tasks are submitted immediately, and we see "All tasks submitted."
  • Two tasks start processing simultaneously.
  • Once a task completes, the thread is reused to start a new task.
  • The process continues until all tasks are finished.
Sample Output
All tasks submitted.
Starting: 0
Starting: 1
Completed: 1
Starting: 2
Completed: 0
Starting: 3
Completed: 2
Starting: 4
Completed: 3
Completed: 4
All tasks completed.