Why use ExecutorService instead of creating threads with new Thread?
Thread creation is costly and unbounded threads can exhaust memory. An ExecutorService reuses a fixed set of worker threads, queues extra tasks, returns Futures, and offers an orderly shutdown.
Explain it simply
Think of an IRCTC counter with four clerks. Hundreds of people can arrive, but only four are served at once and the rest wait in a queue. You do not hire a brand-new clerk for each passenger and send them home afterwards.
Example
import java.util.concurrent.*;
ExecutorService pool = Executors.newFixedThreadPool(2); // two reusable workers
for (int i = 1; i <= 4; i++) {
int id = i;
pool.submit(() -> System.out.println("task " + id + " done"));
}
pool.shutdown(); // no new tasks, finish queued ones
pool.awaitTermination(5, TimeUnit.SECONDS); // wait for completion
System.out.println("all finished"); // prints last
// the order of the four "task" lines is not fixedWhat to say in the interview
"A pool avoids the cost of creating a thread for every task and caps concurrency, which protects the server. Tasks go into a queue, workers pick them up, and submit returns a Future for the result. I must call shutdown() when done, otherwise the non-daemon worker threads keep the JVM alive. In production I prefer constructing ThreadPoolExecutor directly with a bounded queue, because the Executors factory methods can use unbounded queues."
Common mistakes
- Forgetting shutdown(), so the program never exits.
- Using a cached thread pool for unlimited slow tasks, which can create thousands of threads.