Lesson Completion
Back to course

Executor Framework: Managing Thread Pools

Advanced
25 minutes4.6Java

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 TypeSizeUse When
FixedThreadPoolFixedKnown workload, long tasks
CachedThreadPoolGrows as neededMany short tasks
SingleThreadExecutor1 threadSequential execution
ScheduledThreadPoolFixedDelayed/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()); }

Topics Covered

Java FundamentalsConcurrency

Tags

#java#multithreading#threads#concurrency#parallelism

Last Updated

2025-02-01