Topic 5.4
Thread Pools
In one line
ExecutorService, ThreadPoolExecutor, its queue, worker threads and rejection policy — the operational half of concurrency.
Think of it like this
A fixed number of cashiers at a supermarket. If all cashiers (threads) are busy, new customers (tasks) wait in a line (queue); if the line also gets too long, the store may open a temporary counter (extra threads) or turn people away (rejection policy).
Key ideas
- 01
ThreadPoolExecutor(core, max, keepAlive, unit, workQueue, handler) — every parameter is a tuning dial.
- 02
Queue choices: unbounded (LinkedBlockingQueue — safe but can OOM), bounded (ArrayBlockingQueue), SynchronousQueue (hand-off).
- 03
Rejection policies: AbortPolicy (default, throws), CallerRunsPolicy (runs on caller — back-pressure), DiscardOldest, Discard.
- 04
core vs max: pool grows from core→max only when the queue fills (bounded queue semantics).
- 05
Spring: @Async with an injected TaskExecutor config is the bean-level equivalent.
- 06
For LLD: name the queue + rejection policy when you say 'I'll use a pool'.
Java / Spring map
- →
Executors.newFixedThreadPool(n) wraps ThreadPoolExecutor; Executors.newCachedThreadPool ≠ bounded.
Code & diagrams
A realistic production-shaped pool config.
ThreadPoolExecutor pool = new ThreadPoolExecutor(
8, // core threads
32, // max threads
60, TimeUnit.SECONDS, // keep-alive for idle extras
new ArrayBlockingQueue<>(2_000), // bounded → back-pressure instead of OOM
new ThreadPoolExecutor.CallerRunsPolicy() // shed by running in the caller thread
);
// Spring equivalent:
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor e = new ThreadPoolTaskExecutor();
e.setCorePoolSize(8); e.setMaxPoolSize(32);
e.setQueueCapacity(2_000); e.setRejectedExecutionHandler(new CallerRunsPolicy());
return e;
}Explain without notes
Why is 'unbounded queue + max pool' a memory bomb disguised as design?
Practice
Size the pool for a notification fan-out of 100k sends: queue + workers + rejection policy, with reasoning.
Trade-offs
- ↔
Fixed pool = predictable, underutilizes on spikes; dynamic pools adapt but need keep-alive tuning.
Run it in production
You've designed it. Now build, operate, and break the same idea hands-on in the DevOps courses:
Completion checklist
I can read a ThreadPoolExecutor line and explain every parameter.