ExecutorService

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 5 min

TL;DR

  • ExecutorService is a high-level API for managing Thread Pools in Java, abstracting away the low-level complexity of creating and destroying Thread objects manually.
  • Instead of writing new Thread(task).start(), you submit tasks (Runnable or Callable) to the ExecutorService, and it queues and assigns them to its pool of reusable threads.

Concept

Creating a new OS Thread is extremely expensive. It takes milliseconds and requires ~1MB of RAM. If a web server receives 1,000 requests a second, doing new Thread() 1,000 times a second will instantly crash the JVM.

The ExecutorService solves this. When the application starts, it creates a “Pool” of threads (e.g., 100 threads). These threads never die.
When a task comes in, the Executor assigns it to an idle thread. When the task finishes, the thread doesn’t die; it goes back into the pool to wait for the next task.
This provides massive performance gains (no thread creation overhead) and protects the server from crashing by providing a maximum cap on concurrency.

Examples

import java.util.concurrent.*;

public class ExecutorDemo {
    public static void main(String[] args) {
        
        // Creates a pool of exactly 4 reusable threads
        ExecutorService executor = Executors.newFixedThreadPool(4);

        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            
            // We submit 10 tasks. 
            // The 4 threads will process the first 4 tasks.
            // The remaining 6 tasks will wait in a Queue until threads become available.
            executor.submit(() -> {
                System.out.println("Executing Task " + taskId + " on " + Thread.currentThread().getName());
            });
        }

        // IMPORTANT: The JVM will NOT exit until the ExecutorService is shut down!
        // This tells the pool to stop accepting new tasks, finish existing tasks, and terminate.
        executor.shutdown(); 
    }
}

Interview Questions

Q: What is the difference between .shutdown() and .shutdownNow()?
A: - shutdown(): The graceful approach. The Executor refuses any new tasks, but it allows all currently running tasks—and all tasks waiting in the queue—to finish completely before killing the threads.

  • shutdownNow(): The aggressive approach. It instantly halts all waiting tasks in the queue and sends an InterruptedException to all actively running threads, attempting to kill them immediately. It returns a List<Runnable> of all the tasks that were in the queue and never got to execute.

Q: What is Executors.newScheduledThreadPool()?
A: It is a specialized ExecutorService used to run tasks after a delay, or to execute tasks periodically (like a cron job).
For example, executor.scheduleAtFixedRate(task, 0, 10, TimeUnit.MINUTES) will run the task immediately, and then repeatedly every 10 minutes, using a background thread pool. It is the modern replacement for the legacy java.util.Timer class.