Explain the internal working of ThreadPoolExecutor and how it manages tasks in its different states.

ThreadPoolExecutor is the core implementation of Java's Executor framework that efficiently manages task execution using a pool of reusable worker threads. Instead of creating a new thread for every task, it controls thread creation, task queuing, execution, and termination, resulting in better performance and resource utilization.

Key Points: • Tasks are first assigned to available core threads, then queued, and finally executed by additional threads if necessary. • The executor manages worker threads, task queues, thread lifecycle, and rejection policies internally. • ThreadPoolExecutor operates through multiple states such as RUNNING, SHUTDOWN, STOP, TIDYING, and TERMINATED.

Internal Working:

1. Task Submission • A task is submitted through execute() or submit().

2. Core Thread Creation • If the current thread count is less than corePoolSize, a new worker thread is created immediately.

3. Task Queueing • Once all core threads are busy, incoming tasks are placed into the BlockingQueue.

4. Non-Core Thread Creation • If the queue becomes full and the current thread count is less than maximumPoolSize, additional worker threads are created.

5. Rejection Handling • If the queue is full and maximumPoolSize is reached, the configured RejectedExecutionHandler is invoked.

ThreadPoolExecutor States:

• RUNNING - Accepts new tasks and processes queued tasks.

• SHUTDOWN - Stops accepting new tasks but continues processing existing queued tasks.

• STOP - Stops accepting new tasks and attempts to interrupt running tasks.

• TIDYING - All tasks have completed and the executor performs final cleanup activities.

• TERMINATED - Executor has completely shut down.

Example: In an online shopping application, thousands of order-processing requests may arrive simultaneously. ThreadPoolExecutor reuses a limited number of threads, queues excess requests, and prevents the system from being overwhelmed by creating uncontrolled numbers of threads.

Code Example:

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo {

    public static void main(String[] args) {

        ThreadPoolExecutor executor =
                new ThreadPoolExecutor(
                        2,
                        5,
                        60,
                        TimeUnit.SECONDS,

new LinkedBlockingQueue<>(10) );

executor.execute(() ->

                System.out.println(
                        "Task Executed"));

        System.out.println(
                "Pool Size : "
                        + executor.getPoolSize());

        System.out.println(
                "Active Threads : "
                        + executor.getActiveCount());

        executor.shutdown();
    }
}

Interview Tip: A concise interview answer is: ThreadPoolExecutor manages tasks by first assigning them to core threads, then storing them in a work queue, and finally creating additional threads up to maximumPoolSize when needed. It transitions through RUNNING, SHUTDOWN, STOP, TIDYING, and TERMINATED states to control task execution and thread lifecycle efficiently.