ExecutorService in Java with Example

This guide will help you understand the thread pool concept.

In this tutorial, we will create a thread pool using the executor service and the thread pool executor.

I will also show you how to create a single thread executor using executors class.

What you will learn
– What is the Executor Service?
– Difference between a thread and the executor service?
– Executor Service Internals.
– Creating a fixed thread pool with an example.
– Creating a single thread executor with an example.

Introduction to ExecutorService?

In java, it is easy to create a thread using Thread class or Runnable interface.

When you run the above program from the main method, then JVM will create another java thread to execute the task asynchronously.

When you create a thread it won’t run automatically, it runs when thread scheduler decides based on the preemptive or time-slicing scheduling technique.

And once the Thread scheduler allocates CPU to the newly created thread, the new thread will execute the allocated task by executing the run method.

Once thread finishes its task it gets terminated by the JVM.

Although multithreading is good to increase the performance and responsiveness of your application, there is no free lunch.

Each thread maintains its own thread stack.

Therefore, memory allocation has to be done by JVM to initialize and maintain thread stack.

In addition to that, java threads are mapped to native operating system threads.

Hence, system calls are required to create/register java thread with native OS thread.

Thus, it is not advisable to create thousands of threads in a single application to execute jobs asynchronously.

If you require to create plenty of threads in your application then you should sincerely consider creating a fixed number of threads and reusing them.

It may look difficult to implement, but Java already provides the implementation for this.

ExecutorService is the solution to the above problem.

ExecutorService is the one that creates the fixed number of threads in the thread pool.

Executor Service Internals

ExecutorService is an interface.

The actual implementation of ExecutorService is there in its implementing classes.

A most important child class of the executor service is ThreadPoolExecutor.

We use the factory method of Executors class to create an instance of ThreadPoolExecutor.

ExecutorService Fixed Thread Pool
ExecutorService Fixed Thread Pool

This creates the pool of threads that can execute the submitted tasks asynchronously.

Thread pool executor also internally maintains a blocking queue (thread-safe) that keeps all submitted tasks in that queue.

Whenever a task (runnable/callable) is submitted to the thread pool’s blocking queue, one of the available threads from the thread pool will execute the task.

If all threads are busy, the task waits in the blocking queue until a thread is ready to fetch the task from the queue to run it.

If requires we can call the shutdown method of the executor service

Until we call the shutdown method from the executor service, all threads will be alive and ready to take up a task.

Let’s create an example for clear understanding.

ExecutorService Example

We already discussed the executor service internals.

Now, it’s time to get our hands dirty.

Let’s create an example of creating the thread pool using ExecutorService and ThreadPoolExecutor.

Three steps to create the ExecutorService in Java

  1. Create an instance of ExecutorService (ThreadPoolExecutor) using a factory method of Executors class.
    In this factory method, you can provide the maximum number of thread you want to create in the thread pool.
  2. Create a task by implementing the runnable or callable interface.
    For simplicity, you can also use a lambda expression to reduce the boilerplate code.
  3. Submit the task to the executor service by calling execute() method. The task will be executed by one of the available threads in the thread pool.
  4. Shutdown the executor service by calling the shutdown() method.

In this example, we are creating a thread pool of 3 threads.

In addition to that, we have also created two tasks: one using runnable and another one created using the callable interface.

in the line number 14 we have created and submitted the runnable task to executor service.

In line 17 we have created another task using callable and submitted to the executor service.

Since callable returns the value we have used Future<Integer> to get the return from the asynchronous job.

Let’s see what will be the output when we run the above program.

Sequential execution of tasks using Single Thread Executor

Single thread executor is the same as a fixed thread pool however as the name suggests it will have only one thread in the thread pool.

If there are multiple tasks submitted to the fixed thread pool then only one thread will execute tasks one by one.

Single Thread Executor Internal
Single Thread Executor Internal

In case of thread terminates due to some failure then thread pool recreate the thread and execute the task.

The benefit of the single thread executor is that it ensures the sequential execution of the submitted tasks.

Single Thread Executor Example

Tips to create the thread pool.

The number of threads you are creating in a thread pool is important to consider.

You shouldn’t create too few or too many threads in the thread pool.

If you create only 3-4 threads for too many tasks then your application performance will decrease.

At the same time if you create thousands of threads for a few simple operations then your threads might end up waiting for tasks.


In this tutorial, we looked at Executor Service and its internal. We also discussed executor service advantages through the examples.

In case if you have any doubts about executor service or single thread executor then you can reach me via commenting in the comment section.

2 thoughts on “ExecutorService in Java with Example”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.