Why do we need ThreadPools in Java?
We all know how to create Threads in Java.
- Create an implementation of
Runnable - Wrap it inside a
Threadclass and call thestart()method.
Something like this below
Runnable runnableTask = () -> {
System.out.println("Job Started.");
System.out.println("This is running in new thread : " + Thread.currentThread().getName());
System.out.println("Job Completed.");
};
new Thread(runnableTask).start();
If we need more threads, we can create them in a loop.
So why don’t we just create threads on the run, and instead need a ThreadPool?
The answer lies in the fact that creating a Thread is expensive.
Secondly, if we create threads uncontrollably we may run out of these resources quickly.
Having a Threadpool has its advantages
- Reduce Resource Consumption
- Thread pools can reduce the consumption caused by thread creation and destruction by reusing created threads.
- Improve Response Speed
- When a task arrives, the task can be executed immediately without waiting for the thread to be created.
- When a task arrives, the task can be executed immediately without waiting for the thread to be created.
With a Threadpool, we can control the number of threads the application creates and the life cycle of the threads.
Understanding how Java thread pool works
Lets look at the image below
- Applications can submit multiple tasks for execution
- Each task goes into a task queue
- There is a thread pool with “limited number of threads” and each thread picks up a tasks
- Once all threads are busy, remaining tasks are in a “waiting” state – waiting for thread to get free.
- As soon as a thread gets free, it picks up the next tasks in the queue.
- Threads can also return results back on completion

One more illustration for the same

Executor, ExecutorService, ThreadPoolExecutor in Java
Lets first look at the class hierarchy to understand how these three classes relate to each other

Lets understand them one by one
Executor
- An object that executes submitted Runnable tasks.
- Executes the given command at some time in the future. The command may execute in a new thread, in a pooled thread, or in the calling thread, at the discretion of the Executor implementation.
- Has only a single method
void execute(Runnable command);
ExecutorService
ExecutorService extends Executor, AutoCloseable- It is an Executor that provides methods to
- manage termination and
- methods that can produce a Future for tracking progress of one or more asynchronous tasks.
- An ExecutorService can be shut down, which will cause it to reject new tasks.
- Important methods of ExecutorService
submit– extends base methodExecutor. execute(Runnable)by creating and returning a Future that can be used to cancel execution and/ or wait for completion.invokeAnyandinvokeAll– perform the most commonly useful forms of bulk execution, executing a collection of tasks and then waiting for at least one, or all, to complete.shutdown– Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.shutdownNow– Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.awaitTermination– Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
- The
Executorsclass provides factory methods for the executor services provided in this package.e.g.Executors. newFixedThreadPool(...), etc.
ThreadPoolExecutor
ThreadPoolExecutor extends AbstractExecutorService- It is actually an
ExecutorServicethat executes each submitted task using one of possibly several pooled threads, normally configured usingExecutorsfactory methods. - Thread pools address two different problems
- they usually provide improved performance when executing large numbers of asynchronous tasks, due to reduced per-task invocation overhead
- and they provide a means of bounding and managing the resources, including threads, consumed when executing a collection of tasks.
- Each ThreadPoolExecutor also maintains some basic statistics, such as the number of completed tasks.
- Important attributes of a ThreadPoolExecutor
- Core and maximum pool sizes
corePoolSize– the number of threads to keep in the pool, even if they are idle, unlessallowCoreThreadTimeOutis set.maximumPoolSize– the maximum number of threads to allow in the pool.keepAliveTime– when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.
- Core and maximum pool sizes
- New threads are created using a
ThreadFactory.- If not otherwise specified, a
Executors. defaultThreadFactoryis used, that creates threads to all be in the sameThreadGroupand with the sameNORM_PRIORITYpriority and non-daemon status. - By supplying a different
ThreadFactory, you can alter the thread’s name, thread group, priority, daemon status, etc.
- If not otherwise specified, a
- Queuing vs new thread creation
- If fewer than
corePoolSizethreads are running, the Executor always prefers adding a new thread rather than queuing. - If
corePoolSizeor more threads are running, the Executor always prefers queuing a request rather than adding a new thread. - If a request cannot be queued, a new thread is created unless this would exceed
maximumPoolSize, in which case, the task will be rejected.
- If fewer than
In this blog we only covered theoretical concepts about Executors, and ExecutorService and ThreadPoolExecutor.
In future blogs we will cover different types of ThreadPoolExecutor, along with code examples.
