JAVA

intro Executor

neal89 2025. 3. 25. 12:59

☕ Java Thread Pool & Executor Framework – Why and How

When building concurrent applications in Java, you might be tempted to create threads manually using new Thread(...).
But in real-world, large-scale systems, this approach introduces several problems.


🚨 Why Not Create Threads Directly?

1. Performance Cost of Thread Creation

  • Memory usage: Each thread comes with its own call stack, typically taking up 1MB or more of memory.
  • System resource consumption: Creating threads involves system calls, which consume CPU and memory.
  • Scheduling overhead: The OS needs to manage the scheduling of each thread, adding additional overhead.

2. Difficult to Manage

  • Thread lifecycle, synchronization, and exception handling become hard to control.
  • Manual thread creation lacks scalability and maintainability.

3. Runnable Interface is Limited

  • It doesn't return a result.
  • It can't throw checked exceptions.
  • You can't tell when it's done, unless you write extra code.

✅ Why Use the Executor Framework?

The Executor framework provides an abstraction for managing and reusing threads efficiently.

Benefits include:

  • Thread pooling – reuse threads instead of creating new ones every time
  • Task submission – submit Runnable or Callable tasks without worrying about threads
  • Graceful shutdown – stop accepting new tasks and finish all current tasks before shutting down

💡 What is Graceful Shutdown?

Let’s say you’re running a service that handles user orders. If you need to restart the server:

❌ You should not stop it abruptly in the middle of processing orders
✅ You should:

  • Reject new tasks
  • Wait for all running tasks to finish
  • Then shut down

This process is called a graceful shutdown, and ExecutorService supports it easily.


🔧 Example: Using ExecutorService

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ExecutorExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);

        // Submit 3 tasks
        for (int i = 1; i <= 3; i++) {
            int taskId = i;
            executor.submit(() -> {
                System.out.println("Running task " + taskId);
                try {
                    Thread.sleep(1000); // Simulate work
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("Finished task " + taskId);
            });
        }

        // Graceful shutdown
        executor.shutdown();
        try {
            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                executor.shutdownNow(); // Force shutdown if not done in time
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }

        System.out.println("All tasks completed. Executor shut down.");
    }
}

✅ Summary

❌ Manual Threads ✅ Executor Framework

High memory usage Reuses threads efficiently
Hard to manage lifecycle Easy to submit & control
No result or error flow Supports Callable, Future
No graceful shutdown Built-in shutdown support

If you're building scalable, stable applications, using the Executor framework is not just a good idea — it's essential.