Thread Pool in Java

Last Updated : 9 Jan 2026

In Java, thread pool is a group of pre-created, reusable worker threads that are waiting to perform a task. Thread pool is managed by the JVM. Instead of creating a new thread for each task, tasks are submitted to the pool, where available threads execute them. It improves performance, reduces overhead, and ensures better resource management.

To read more How to Create Thread in Java

There are the following three conditions when a task is submitted:

  • If a thread is free, it executes the task immediately.
  • If all the threads are busy, the task waits in a queue until a thread becomes available.
  • After completing a task, the thread returns to the pool, not terminated.

Why use thread pool?

  • Performance: It avoids the cost of creating/destroying threads repeatedly.
  • Resource Management: It limits the number of concurrent threads, preventing system overload.
  • Scalability: It efficiently handles large numbers of short-lived tasks.

Executer Thread Pool Methods

newFixedThreadPool(int s): The method creates a thread pool of the fixed size s.

newCachedThreadPool(): The method creates a new thread pool that creates the new threads when needed but will still use the previously created thread whenever they are available to use.

newSingleThreadExecutor(): The method creates a new thread.

Advantages of Java Thread Pool

  • The main advantage of thread pool is efficient thread management.
  • It reduces the overhead of creating and destroying threads, improves application performance, and provides better control over concurrency.
  • Threads are pre-created and reused, avoiding the costly process of repeatedly creating and destroying threads.
  • It leads to faster execution of short-lived tasks.
  • Limits the number of concurrent threads, preventing excessive memory and CPU usage.
  • It helps to avoid issues like thread starvation or system crashes due to uncontrolled thread creation.
  • Developers can define the maximum number of threads in the pool.
  • Ensures predictable behavior under heavy load by queuing tasks when all threads are busy.
  • After completing a task, a thread does not die; it returns to the pool ready for the next task.
  • Thread pools prevent applications from being overwhelmed by too many threads.
  • They provide a safer way to handle large numbers of tasks without risking system instability.
  • By balancing workload across a fixed number of threads, applications achieve higher throughput and responsiveness.

Example: Java Thread Pool

Let's see a simple example of the Java thread pool using ExecutorService and Executors.

File Name: Main.java

Output:

pool-1-thread-1 (Start) message = 0
pool-1-thread-2 (Start) message = 1
pool-1-thread-3 (Start) message = 2
pool-1-thread-5 (Start) message = 4
pool-1-thread-4 (Start) message = 3
pool-1-thread-2 (End)
pool-1-thread-2 (Start) message = 5
pool-1-thread-1 (End)
pool-1-thread-1 (Start) message = 6
pool-1-thread-3 (End)
pool-1-thread-3 (Start) message = 7
pool-1-thread-4 (End)
pool-1-thread-4 (Start) message = 8
pool-1-thread-5 (End)
pool-1-thread-5 (Start) message = 9
pool-1-thread-2 (End)
pool-1-thread-1 (End)
pool-1-thread-4 (End)
pool-1-thread-3 (End)
pool-1-thread-5 (End)
Finished all threads

Example: Thread Pool

Example

Compile and Run

Output:

Initialization time for the task name: task 1 = 12 : 14 : 02
Initialization time for the task name: task 3 = 12 : 14 : 02
Initialization time for the task name: task 2 = 12 : 14 : 02
Time of execution for the task name: task 1 = 12 : 14 : 04
Time of execution for the task name: task 3 = 12 : 14 : 04
Time of execution for the task name: task 2 = 12 : 14 : 04
Time of execution for the task name: task 1 = 12 : 14 : 05
Time of execution for the task name: task 3 = 12 : 14 : 05
Time of execution for the task name: task 2 = 12 : 14 : 05
Time of execution for the task name: task 1 = 12 : 14 : 06
Time of execution for the task name: task 3 = 12 : 14 : 06
Time of execution for the task name: task 2 = 12 : 14 : 06
Time of execution for the task name: task 1 = 12 : 14 : 07
Time of execution for the task name: task 3 = 12 : 14 : 07
Time of execution for the task name: task 2 = 12 : 14 : 07
Time of execution for the task name: task 1 = 12 : 14 : 08
Time of execution for the task name: task 3 = 12 : 14 : 08
Time of execution for the task name: task 2 = 12 : 14 : 08
task 1 is complete.
task 3 is complete.
Initialization time for the task name: task 4 = 12 : 14 : 09
Initialization time for the task name: task 5 = 12 : 14 : 09
task 2 is complete.
Time of execution for the task name: task 4 = 12 : 14 : 10
Time of execution for the task name: task 5 = 12 : 14 : 10
Time of execution for the task name: task 4 = 12 : 14 : 11
Time of execution for the task name: task 5 = 12 : 14 : 11
Time of execution for the task name: task 4 = 12 : 14 : 12
Time of execution for the task name: task 5 = 12 : 14 : 12
Time of execution for the task name: task 4 = 12 : 14 : 13
Time of execution for the task name: task 5 = 12 : 14 : 13
Time of execution for the task name: task 4 = 12 : 14 : 14
Time of execution for the task name: task 5 = 12 : 14 : 14
task 4 is complete.
task 5 is complete.

Explanation: It is evident by looking at the output of the program that tasks 4 and 5 are executed only when the thread has an idle thread. Until then, the extra tasks are put in the queue.

The takeaway from the above example is when one wants to execute 50 tasks but is not willing to create 50 threads. In such a case, one can create a pool of 10 threads. Thus, 10 out of 50 tasks are assigned, and the rest are put in the queue. Whenever any thread out of 10 threads becomes idle, it picks up the 11th task. The other pending tasks are treated the same way.

Risks and Considerations in Thread Pools

The following are the risk involved in the thread pools:

Deadlock: It is a known fact that deadlock can come in any program that involves multithreading, and a thread pool introduces another scenario of deadlock. Consider a scenario where all the threads that are executing are waiting for the results from the threads that are blocked and waiting in the queue because of the non-availability of threads for the execution.

Thread Leakage: Leakage of threads occurs when a thread is being removed from the pool to execute a task but is not returning to it after the completion of the task. For example, when a thread throws the exception and the pool class is not able to catch this exception, then the thread exits and reduces the thread pool size by 1. If the same thing repeats a number of times, then there are fair chances that the pool will become empty, and hence, there are no threads available in the pool for executing other requests.

Resource Thrashing: A lot of time is wasted in context switching among threads when the size of the thread pool is very large. Whenever there are more threads than the optimal number may cause the starvation problem, and it leads to resource thrashing.

Tuning the Thread Pool

The accurate size of a thread pool is decided by the number of available processors and the type of tasks the threads have to execute. If a system has the P processors that have only got the computation type processes, then the maximum size of the thread pool of P or P + 1 achieves the maximum efficiency. However, the tasks may have to wait for I/O, and in such a scenario, one has to take into consideration the ratio of the waiting time (W) and the service time (S) for the request; resulting in the maximum size of the pool P * (1 + W / S) for the maximum efficiency.

Conclusion

A thread pool is a very handy tool for efficient concurrency management, especially on the server-side. Concept-wise, a thread pool is very easy to comprehend. However, one may have to look at a lot of issues when dealing with a thread pool. It is because the thread pool comes with some risks involved it (risks are discussed above).