Thread Pool in Java



Java Thread Pool

Thread pool in Java

In Java, a thread pool is a group of threads that we can reuse for various tasks in a multithreading environment. The thread pool may be of a fixed size where each thread performs a task and once it completes it again goes back to the pool. In this way, each thread can execute more than 1 task. This reduces the overhead of creating multiple threads for different tasks. We can also restrict the number of active threads by creating a fixed-size thread pool. Java contains an in-built thread pool called ThreadPoolExecutor which we will discuss in detail in this article.

Advantages of a Thread pool

  • Better performance
  • Less resource required
  • Reduced overhead
  • More responsive

Disadvantages of a Thread pool

  • Chances of deadlock
  • Thread leakage
  • Resource thrashing

ThreadPoolExecutor

Java has an in-built thread pool class named ThreadPoolExecutor that implements the Executor interface and ExecutorService subinterface. To use this, we need to implement the Runnable object and pass it to the Executor which will execute the task. The ExecutorService object contains the set of tasks that we want to process. The Java thread pool initializes the size of the pool and the threads sequentially execute the tasks as and when it is free.

Thread Pool in Java

In the above illustration, we have a ThreadBolockingQueue with 5 tasks waiting in line for execution. The thread pool has a fixed size of 3 threads. Each thread executes 1 task at a time and when a thread becomes free, it sequentially takes the next available task for execution.

You can see in the below diagram that Thread 1,2 and 3 execute Task 1,2 and 3. Meanwhile, Task 4 and 5 waits in the queue until any of the threads complete the execution. When any thread is idle, it picks up the next task from the queue and completes the execution.

ThreadPoolExecutor

Methods of Executor

Below are the main methods of an Executor class that is part of the java.util.concurrent.Executors interface.

MethodDescription
newFixedThreadPool(int n)Creates a thread pool with a fixed number of reusable threads.
This is the best option to use
newCachedThreadPool()Creates a thread pool that creates new threads. It also reused previously created threads when available.
This method not advisable to use if tasks are long running
newScheduledThreadPool(long ms)Creates a thread pool that schedules the execution after a specific delay or time period
newSingleThreadExecutor()Creates a thread pool with a single thread that performs all the tasks
newWorkStealingPool()Creates a thread pool with the specified number of threads that supports parallelism

Methods of the ThreadPoolExecutor

Below are the most commonly used methods of the ThreadPoolExecutor class.

MethodDescription
boolean awaitTermination(long timeout, TimeUnit unit)Blocks until all threads complete execution after shutdown
void execute(Runnable command)Executes the task
int getActiveCount()Returns the number of actively executing threads
long getCompletedTaskCount()Returns the number of tasks that completed the execution
int getCorePoolSize()Returns the number of core threads
int getMaximumPoolSize()Returns the maximum number of allowed threads
int getPoolSize()Returns the current number of threads
long getTaskCount()Returns the number of tasks scheduled for execution
boolean isShutDown()Returns true if the executor is shutdown
boolean isTerminated()Returns true if all tasks have completed execution after shutdown
boolean isTerminating()Returns true if the executor is in the process of terminating after shutdown
int prestartAllCoreThreads()Starts all core threads after idle waiting time
void purge()Tries to remove the tasks from the queue that have been cancelled
boolean remove(Runnable command)Removes the specified task from the queue of the executor
void setCorePoolSize()Sets the number of core threads
void setMaximumPoolSize(int maxpoolsize)Sets the maximum number of allowed threads in the pool
void shutdown()Shuts down the executor
List shutdownNow()Attempts to stop all actively executing tasks

Example: FixedThreadPool

Below is a simple example of using the newFixedThreadPool method of the ThreadPoolExecutor class. The SampleTask class performs the task where it prints the task name and some random value.

The FixedThreadPoolDemo class is the main class where we execute all the tasks using a fixed size thread pool in Java. We create a thread pool using the newFixedThreadPool method that initializes the size to 3. We then create 5 tasks where these 3 threads execute all the 5 tasks as and when the thread is available.

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

class SampleTask implements Runnable {

  private String name;
  
  public SampleTask(String name) {
    this.name = name;
  }
  
  public String getTaskName() {
    return name;
  }
  
  //Task
  public void run() {
    try {
      System.out.println("Executing Task: " + name);
      int rand = (int)(Math.random()*15);
      System.out.println("Random value for " + name + ": " + rand);
    }
    catch(Exception e) {
      e.printStackTrace();
    }
    
  }
  
}

public class FixedThreadPoolDemo {

  public static void main(String[] args) {
    
    //Create a thread pool with 3 threads
    ThreadPoolExecutor ex = (ThreadPoolExecutor)Executors.newFixedThreadPool(3);
    
    //Execute 5 tasks using 3 threads
    for(int i=1;i<=5;i++) {
      SampleTask t = new SampleTask("Task " + i);
      System.out.println("Task started: "+ t.getTaskName());
      
      ex.execute(t);
    }
    ex.shutdown();
  }

}
Task started: Task 1
Task started: Task 2
Task started: Task 3
Executing Task: Task 1
Executing Task: Task 2
Task started: Task 4
Executing Task: Task 3
Task started: Task 5
Random value for Task 1: 6
Random value for Task 2: 13
Executing Task: Task 4
Random value for Task 3: 11
Random value for Task 4: 1
Executing Task: Task 5
Random value for Task 5: 7

Example: ScheduledThreadPoolExecutor

Whenever we want to execute the same task repeatedly after a fixed delay, we can use the newScheduledThreadPoolExecutor method of the ThreadPoolExecutor class. In the below example, we create a scheduled Java thread pool of size 3 and repeatedly execute the same task that we define in the TaskDemo class every 3 seconds. The TaskDemo class prints the current execution time so that we can see the delay in execution.

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadPoolDemo {

  public static void main(String[] args) {
    ScheduledThreadPoolExecutor se = (ScheduledThreadPoolExecutor)Executors.newScheduledThreadPool(3);
    
    TaskDemo t = new TaskDemo("task");
    System.out.println("Task created");
    
    se.scheduleWithFixedDelay(t, 3, 3, TimeUnit.SECONDS);
  }

}

class TaskDemo implements Runnable {

  private String name;
  public TaskDemo(String name) {
    this.name = name;
  }
  
  public String getTaskName() {
    return name;
  }
  
  public void run() {
    System.out.println("Executing " + name + " at current time: " + new Date());	
    
  }
  
}
Task created
Executing task at current time: Fri Feb 12 06:47:58 IST 2021
Executing task at current time: Fri Feb 12 06:48:02 IST 2021
Executing task at current time: Fri Feb 12 06:48:05 IST 2021
Executing task at current time: Fri Feb 12 06:48:08 IST 2021
Executing task at current time: Fri Feb 12 06:48:11 IST 2021

 

Reference