How to create threads using the Executors class in Java

Thread pool

A thread pool is a group of threads that are created in advance and stored in a pool (like a queue). When needed, the threads are taken out from the pool and assigned to tasks. After finishing their task, they are returned back to the pool.

Thread pool

Thread pools are used to reduce the overhead of thread creation and management. Using a thread pool, we can avoid creating and destroying threads frequently, which reduces the performance overhead.

The Executors class

The Executors class provides several static methods that can be used to create and manage a thread pool. The most commonly used method is the newFixedThreadPool() method, which creates a fixed-size thread pool.

Example 1

Let's see how to use the newFixedThreadPool() method to create a thread pool.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class ThreadPoolExample {
public static void main(String[] args) {
// create a fixed size thread pool with 5 threads
ExecutorService service = Executors.newFixedThreadPool(5);
// submit tasks to be executed by the pool
for (int i = 0; i < 10; i++) {
service.submit(new Task(i));
}
// shutdown the pool
service.shutdown();
}
}
class Task implements Runnable {
private int taskId;
public Task(int id) { this.taskId = id; }
public void run() {
System.out.println("Starting task " + taskId);
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Finishing task " + taskId);
}
}

Explanation

  • Lines 1–2: We import the ExecutorService and Executors classes.
  • Line 4: We create a ThreadPoolExample class.
  • Line 7: We create a fixed-size thread pool with 5 threads.
  • Line 10: We submit tasks to be executed by the pool. In this example, we are just creating 10 tasks.
  • Line 15: We shut down the pool. This will wait for all tasks to complete before shutting down.
  • Line 19: We create a Task class that implements Runnable. A task is just a Runnable that we submit to an ExecutorService.
  • Line 20: We have a taskId field that is used to track the task.
  • Line 22: We have a constructor that takes an id and sets the taskId field.
  • Line 24: The run() method is where we implement the task logically. In this example, we just print a message and sleep for 100 milliseconds.
  • Line 33: We finish by printing a message.

Example 2

The newSingleThreadExecutor() methods of Executors class creates an executor with a single thread. The example below demonstrating the use of this method:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class ThreadPoolExample {
public static void main(String[] args) {
// create a new executor with a single thread
ExecutorService service = Executors.newSingleThreadExecutor();
// submit tasks to be executed by the pool
for (int i = 0; i < 10; i++) {
service.submit(new Task(i));
}
// shutdown the pool
service.shutdown();
}
}
class Task implements Runnable {
private int taskId;
public Task(int id) { this.taskId = id; }
public void run() {
System.out.println("Starting task " + taskId);
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Finishing task " + taskId);
}
}

Explanation

  • Line 7: We create a single-threaded ExecutorService using the newSingleThreadExecutor() method.
  • Lines 10–12: We submitted 10 tasks to be executed by the pool.
  • Line 15: We shut down the pool. This will wait for all tasks to complete before shutting down.
  • Line 19: We create a Task class that implements Runnable.
  • Line 33: We finish by printing a message.

Example 3

The newCachedThreadPool() creates a thread pool that uses a limited number of threads that are kept alive as long as there are tasks to execute.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class ThreadPoolExample {
public static void main(String[] args) {
// create a new cached thread pool
ExecutorService service = Executors.newCachedThreadPool();
// submit tasks to be executed by the pool
for (int i = 0; i < 10; i++) {
service.submit(new Task(i));
}
// shutdown the pool
service.shutdown();
}
}
class Task implements Runnable {
private int taskId;
public Task(int id) { this.taskId = id; }
public void run() {
System.out.println("Starting task " + taskId);
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Finishing task " + taskId);
}
}

Explanation

  • Line 7: We create a cached thread pool using the newCachedThreadPool() method. This will create a pool of threads that can grow and shrink as needed.
  • Line 10–12: We submit 10 tasks to be executed by the pool.
  • Line 15: We shut down the pool. This will wait for all tasks to complete before shutting down.
  • Line 19: We create a Task class that implements Runnable.
  • Line 33: We finish by printing a message.

Free Resources