Table of Contents
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.
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.
Methods of Executor
Below are the main methods of an Executor class that is part of the java.util.concurrent.Executors
interface.
Method | Description |
---|---|
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.
Method | Description |
---|---|
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