Custom Thread pool implementation in Java

Upasana | July 25, 2020 | 3 min read | 4,666 views


Thread pool executor requires a Queue for holding tasks and a collection of Worker Threads that will pick up tasks from the work queue start running them. Let us try to write our own simple Thread Pool Executor implementation. It is a typical Producer Consumer Problem statement.

threadpool executor
Custom ThreadPoolExecutor

Below Java Program provides basic implementation for a working proof of concept for a threadpool executor.

CustomThreadPoolExecutor Basic Implementation
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class CustomThreadPoolExecutor {
    private final BlockingQueue<Runnable> workerQueue;
    private final Thread[] workerThreads;

    public CustomThreadPoolExecutor(int numThreads) {
        workerQueue = new LinkedBlockingQueue<>();
        workerThreads = new Thread[numThreads];
        int i = 0;
        for (Thread t : workerThreads) {
            t = new Worker("Custom Pool Thread " + ++i);
            t.start();
        }
    }

    public void addTask(Runnable r) {
        try {
            workerQueue.put(r);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    class Worker extends Thread {
        public Worker(String name) {
            super(name);
        }

        public void run() {
            while (true) {
                try {
                    workerQueue.take().run();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        CustomThreadPoolExecutor threadPoolExecutor = new CustomThreadPoolExecutor(10);
        threadPoolExecutor.addTask(() -> System.out.println("First print task"));
        threadPoolExecutor.addTask(() -> System.out.println("Second print task"));
    }

}

The above program will create pool of 10 worker threads and initialize them all.

Explanation

We are using two classes from standard java library in this implementation:

LinkedBlockingQueue

An optionally-bounded blocking queue based on linked nodes. This queue orders elements FIFO (first-in-first-out). It is thread-safe in nature and acts as a temporary storage of runnable tasks that are due for execution.

Thread

All the threads get initilized and started at the creation of ThreadPoolExecutor. All threads listen on the shared workqueue for incoming tasks in never ending loop.

What we missed

An production grade implementation would have lot more sophistication than the basic implementation provided here, for example:

  1. Capability to tune number of items that can fit into queue.

  2. Capability to configure idle and max worker threads, incase of no work items, number of active threads will reduce to idle threads.

  3. Lazy initialization

  4. Proper exception handling is not implemented in this article

Production usage advise

Always use Executor Framework provided in java.util.concurrent package for executing tasks in your production code. It is simple to use, provides interface-based task execution facility and time tested.

ExecutorService example usage
ExecutorService exec = Executors.newSingleThreadExecutor();
exec.execute(runnable);
exec.shutdown();

Why do we need ThreadPool executor?

There are many reasons one would like to use threadpool executor:

  1. Creating and destroying threads is a IO extensive operation, which has impact on performance and memory consumption of an application. So its ideal to create threads once and reuse them later on.

  2. We do not want to run out of threads when heavy load arrives on an application. Threadpool holds tasks in a queue, so if lot of tasks arrives in a very short amount of time, queue will hold the tasks until a worker thread becomes available for the processing. This approach prevents resource exhaustion in production environment.

  3. If due to some reasons, thread gets killed, ThreadPoolExecutor will recreate the thread and put it back to the pool.


Top articles in this category:
  1. Java 8 Parallel Stream custom ThreadPool
  2. How will you implement your custom threadsafe Semaphore in Java
  3. ThreadLocal with examples in Java
  4. Difference between Implementing Runnable and Extending Thread
  5. What will happen if we don't synchronize getters/accessors of a shared mutable object in multi-threaded applications
  6. Troubleshooting Deadlock in Java
  7. Discuss internals of a ConcurrentHashmap (CHM) in Java

Recommended books for interview preparation:

Find more on this topic:
Buy interview books

Java & Microservices interview refresher for experienced developers.