1. The Hook (The "Byte-Sized" Intro)
- In a Nutshell: Executor Framework (java.util.concurrent) manages thread pools automatically—submit tasks, don't create threads! ExecutorService provides lifecycle (shutdown, submit), ThreadPoolExecutor customizes pool size/queues.
- Pre-built pools: FixedThreadPool (fixed size), CachedThreadPool (grows as needed), SingleThreadExecutor (one thread), ScheduledThreadPool (delayed/periodic tasks). Always shutdown() executors or threads leak!
Think of taxi company. Without executors = hire new driver per customer (expensive!). With executors = thread pool = fleet of drivers who handle multiple customers (reuse)!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy: The Task Queue
Executor = restaurant with fixed number of chefs (threads). Orders (tasks) go to queue. Chefs process orders from queue. No chef = order waits!
3. Technical Mastery (The "Deep Dive")
Without Executors (Manual Thread Management)
java
// ❌ BAD: Create new thread per task
for (int i = 0; i < 1000; i++) {
new Thread(() -> doWork()).start(); // 1000 threads!
}With Executors (Thread Pool)
java
// ✅ GOOD: Reuse threads from pool
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executor.submit(() -> doWork()); // Only 10 threads!
}
executor.shutdown();4. Interactive & Applied Code
java
import java.util.concurrent.*;
import java.util.*;
public class ExecutorDemo {
public static void main(String[] args) throws Exception {
demonstrateFixedThreadPool();
demonstrateCachedThreadPool();
demonstrateScheduledExecutor();
demonstrateCallableWithFuture();
}
static void demonstrateFixedThreadPool() throws Exception {
System.out.println("=== FIXED THREAD POOL ===");
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 1; i <= 6; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " by " +
Thread.currentThread().getName());
try { Thread.sleep(1000); } catch (Exception e) {}
});
}
executor.shutdown(); // Stop accepting new tasks
executor.awaitTermination(10, TimeUnit.SECONDS); // Wait for completion
}
static void demonstrateCachedThreadPool() {
System.out.println("\n=== CACHED THREAD POOL ===");
ExecutorService executor = Executors.newCachedThreadPool();
// Creates threads as needed, reuses idle threads
// Ideal for short-lived tasks
for (int i = 0; i < 5; i++) {
executor.execute(() -> {
System.out.println("Task by " + Thread.currentThread().getName());
});
}
executor.shutdown();
}
static void demonstrateScheduledExecutor() {
System.out.println("\n=== SCHEDULED EXECUTOR ===");
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// Schedule one-time task
scheduler.schedule(() -> {
System.out.println("Delayed task after 2 seconds");
}, 2, TimeUnit.SECONDS);
// Schedule periodic task
scheduler.scheduleAtFixedRate(() -> {
System.out.println("Periodic task: " + System.currentTimeMillis());
}, 0, 1, TimeUnit.SECONDS); // Initial delay 0, period 1 second
// Let it run for 5 seconds, then shutdown
try { Thread.sleep(5000); } catch (Exception e) {}
scheduler.shutdownNow();
}
static void demonstrateCallableWithFuture() throws Exception {
System.out.println("\n=== CALLABLE WITH FUTURE ===");
ExecutorService executor = Executors.newFixedThreadPool(2);
Callable<Integer> task = () -> {
Thread.sleep(1000);
return 42;
};
Future<Integer> future = executor.submit(task);
System.out.println("Task submitted, doing other work...");
// Blocking call - waits for result
Integer result = future.get(); // Blocks here!
System.out.println("Result: " + result);
// With timeout
try {
Future<Integer> future2 = executor.submit(task);
Integer result2 = future2.get(500, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
System.out.println("Task timed out!");
}
executor.shutdown();
}
}
// Custom ThreadPoolExecutor
class CustomExecutorDemo {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // Core pool size
4, // Max pool size
60, TimeUnit.SECONDS, // Keep-alive time
new LinkedBlockingQueue<>(10) // Work queue
);
System.out.println("Core pool size: " + executor.getCorePoolSize());
System.out.println("Max pool size: " + executor.getMaximumPoolSize());
executor.shutdown();
}
}5. The Comparison & Decision Layer
| Pool Type | Size | Use When |
|---|---|---|
| FixedThreadPool | Fixed | Known workload, long tasks |
| CachedThreadPool | Grows as needed | Many short tasks |
| SingleThreadExecutor | 1 thread | Sequential execution |
| ScheduledThreadPool | Fixed | Delayed/periodic tasks |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "What happens if you forget to shutdown() an ExecutorService?" Answer: Thread leak! Executor's threads keep JVM alive—program never exits!
java
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(() -> System.out.println("Task"));
// ❌ Forgot shutdown() - JVM hangs forever!
// ✅ ALWAYS shutdown
try {
executor.shutdown();
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow(); // Force shutdown
}
} catch (InterruptedException e) {
executor.shutdownNow();
}Pro-Tip: submit() vs execute():
java
// execute(): Fire-and-forget (no return value)
executor.execute(() -> doWork());
// submit(): Returns Future (can get result/exception)
Future<?> future = executor.submit(() -> doWork());
try {
future.get(); // Can catch exceptions!
} catch (ExecutionException e) {
System.out.println("Task failed: " + e.getCause());
}