Walk through how `ThreadPoolExecutor` handles a new task. Why does `new ThreadPoolExecutor(5, 50, 60, SECONDS, new LinkedBlockingQueue<>())` never grow past 5 threads?
advancedCore Java › Multithreading & Concurrency
The executor tries three things in a fixed order: start a core thread, then enqueue, and only when the queue refuses the task start a non-core thread up to maximumPoolSize; if that also fails the RejectedExecutionHandler runs. With an unbounded queue offer() never refuses, so the pool is stuck at corePoolSize and maximumPoolSize is dead configuration.
workers < corePoolSize: spawn a worker for this task, even if other core threads are idle.- Otherwise
workQueue.offer(task); success means an idle worker willtake()it later. - Queue full and
workers < maximumPoolSize: spawn a non-core worker (reclaimed afterkeepAliveTime). - Otherwise reject:
AbortPolicy(throws),CallerRunsPolicy(caller executes, natural backpressure),DiscardPolicy,DiscardOldestPolicy.
ExecutorService pool = new ThreadPoolExecutor(
5, 50, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(200), // bounded: max threads become reachable
new ThreadPoolExecutor.CallerRunsPolicy()); // slows producers instead of droppingUnder a spike with the unbounded queue the symptom is rising latency and heap (queued tasks), not rejected tasks. With a bounded queue the pool reaches 5 + 50 threads only after 200 tasks are queued.
- Does a
SynchronousQueue(as innewCachedThreadPool) behave the same? No. It has no capacity, so every task beyond idle workers spawns a thread up tomaximumPoolSize(unbounded for cached pools), a different way to run out of resources. - Do core threads time out? Not by default; call
allowCoreThreadTimeOut(true)to let idle core threads expire too. - Is
Executors.newFixedThreadPoolsafe in production? It uses an unboundedLinkedBlockingQueue, so overload becomes memory growth.