A thread pool is a collection of pre-created, reusable threads managed by the JVM.
Instead of creating a new thread for every task:
- Threads are created once
- Tasks are submitted to the pool
- Idle threads pick up tasks
- Threads are reused, not destroyed
This is implemented in Java using the Executor Framework (java.util.concurrent).
Creating and destroying threads repeatedly is expensive.
new Thread(task).start();Issues:
- High memory usage
- Slow thread creation
- Too many threads → context switching overhead
- Possible
OutOfMemoryError - No lifecycle management
- Reduced thread creation overhead
- Better CPU utilization
- Controlled concurrency
- Improved performance and scalability
- Centralized thread management
- Built-in task queuing
Java introduced the Executor Framework in Java 5.
Key interfaces and classes:
Executor
↓
ExecutorService
↓
ThreadPoolExecutor
public interface Executor {
void execute(Runnable command);
}It separates task submission from task execution.
Adds lifecycle management:
- Submit tasks
- Shutdown pool
- Track task completion
Common methods:
submit()
shutdown()
shutdownNow()
isShutdown()
isTerminated()Key components inside a thread pool:
- Worker threads
- Task queue
- Thread factory
- Rejection policy
Java provides factory methods via Executors class.
ExecutorService executor =
Executors.newFixedThreadPool(5);- Fixed number of threads
- Tasks wait in queue if all threads are busy
- Threads are reused
- CPU-bound tasks
- Stable number of concurrent requests
ExecutorService executor =
Executors.newCachedThreadPool();- Creates new threads as needed
- Reuses idle threads
- No upper bound on threads
- Short-lived, lightweight tasks
- I/O-bound tasks
- Can create too many threads → memory issues
ExecutorService executor =
Executors.newSingleThreadExecutor();- One worker thread
- Tasks executed sequentially
- Maintains order
- Logging
- Sequential background tasks
ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(2);scheduler.schedule(task, 5, TimeUnit.SECONDS);
scheduler.scheduleAtFixedRate(task, 0, 5, TimeUnit.SECONDS);- Timers
- Periodic background jobs
executor.execute(() -> {
System.out.println(Thread.currentThread().getName());
});- No return value
- Fire-and-forget tasks
Future<Integer> result =
executor.submit(() -> 10 + 20);- Returns a
Future - Can retrieve result or exception
Integer value = result.get();| Runnable | Callable |
|---|---|
| No return value | Returns a value |
| Cannot throw checked exception | Can throw checked exception |
execute() / submit() |
submit() |
Thread pools must be shut down properly.
executor.shutdown();- Stops accepting new tasks
- Completes existing tasks
executor.shutdownNow();- Attempts to stop running tasks
- Interrupts threads
- Returns pending tasks
executor.shutdown();
try {
if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}| Feature | Thread Pool | Manual Threads |
|---|---|---|
| Thread reuse | Yes | No |
| Resource control | Yes | No |
| Performance | High | Low |
| Scalability | High | Poor |
| Error handling | Centralized | Scattered |
- Creating too many threads
- Forgetting to shut down pool
- Using cached pool blindly
- Blocking tasks in small pools
- Wrong pool size for workload
Pool size ≈ Number of CPU cores
Pool size > Number of CPU cores
Rule of thumb:
Threads = CPU cores * (1 + wait time / compute time)
- Web servers (Tomcat, Spring Boot)
- Database connection handling
- Async processing
- Background jobs
- Message consumers
A collection of reusable threads that execute submitted tasks.
To reduce thread creation overhead and improve performance.
execute() → no result
submit() → returns Future
JVM may not terminate; memory leaks occur.
- core → minimum threads
- max → upper limit during high load
When pool cannot accept more tasks due to capacity limits.
Fixed thread pool.
Cached thread pool (unbounded threads).
-
Thread pools reuse threads to improve performance
-
ExecutorService manages lifecycle and execution
-
Fixed, Cached, Single, and Scheduled pools serve different needs
-
Proper shutdown is critical
-
ThreadPoolExecutor gives full control
-
Choosing correct pool size is essential for scalability
