Thread Pool in Java
A thread pool is a collection of pre-created worker threads that execute submitted tasks, reusing threads to avoid the overhead of creating and destroying threads for each task. Java provides built-in thread-pool support in the java.util.concurrent package.
Key benefits
- Reduced latency: avoid thread creation cost for each task.
- Controlled concurrency: limit number of concurrent threads to match resources.
- Resource management: prevents thread explosion that can exhaust memory/CPU.
- Task queuing: tasks wait in a queue when all threads are busy.
Core concepts
- Worker threads: threads that repeatedly take tasks from a queue and run them.
- Task (Runnable/Callable): units of work submitted to the pool.
- Task queue: holds waiting tasks (bounded or unbounded).
- Pool size: number of active worker threads (core and maximum).
- Keep-alive time: time non-core threads may remain idle before terminating.
- RejectedExecutionHandler: policy for handling tasks when the queue is full and pool is at max.
Java APIs
- Executors factory methods (java.util.concurrent.Executors):
- Executors.newFixedThreadPool(n): fixed-size pool.
- Executors.newCachedThreadPool(): dynamic pool that creates threads as needed and reuses idle ones.
- Executors.newSingleThreadExecutor(): single-worker pool.
- Executors.newScheduledThreadPool(n): for delayed/periodic tasks.
- Core classes/interfaces:
- Executor: single-method execute(Runnable).
- ExecutorService: extends Executor; supports lifecycle (submit, shutdown, awaitTermination).
- ScheduledExecutorService: supports scheduling tasks.
- ThreadPoolExecutor: configurable concrete implementation.
- Future / FutureTask: represent task results and allow cancellation.
Typical ThreadPoolExecutor constructor parameters
- corePoolSize — minimum number of threads to keep.
- maximumPoolSize — maximum allowed threads.
- keepAliveTime & unit — idle time before reclaiming extra threads.
- workQueue — queue implementation (e.g., LinkedBlockingQueue, ArrayBlockingQueue, SynchronousQueue).
- threadFactory — creates new threads.
- handler — RejectedExecutionHandler when saturated (AbortPolicy, CallerRunsPolicy, DiscardPolicy, DiscardOldestPolicy).
Example: fixed thread pool
ExecutorService pool = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) {
final int id = i;
pool.submit(() -> {
System.out.println("Task " + id + " running in " + Thread.currentThread().getName());
try { Thread.sleep(500); } catch (InterruptedException ignored) {}
});
}
pool.shutdown();
Best practices
- Prefer Executors factory methods for common use-cases; use ThreadPoolExecutor for fine control.
- Choose queue type based on desired behavior: bounded queue for backpressure, SynchronousQueue for direct handoff.
- Set sensible pool sizes: typically related to number of CPU cores (IO-bound vs CPU-bound tasks differ).
- Always shut down ExecutorService (shutdown or shutdownNow) to allow JVM exit.
- Use Future to get results or cancel tasks; prefer CompletableFuture for composition.
- Avoid unbounded queues with unbounded task submission — can lead to OOM.
Top comments (0)