Java · OOP, JVM and backend craft

How do thread pools work and how do you size them?

Mediumthread poolexecutorservicevirtual threadsUpdated Oct 2026
Quiz this track

Short answer

A thread pool reuses a bounded set of worker threads to run tasks. It controls concurrency, queueing and rejection so the application does not create unbounded threads.

ThreadPoolExecutor has a core pool size, a maximum pool size, a work queue and a rejection policy. Tasks first use core threads, then queue, then grow toward the maximum, and finally hit rejection. Executors offers presets, but the presets hide these parameters.

java
ExecutorService executor = Executors.newFixedThreadPool(8);
try {
    Future<Integer> result = executor.submit(() -> 40 + 2);
    System.out.println(result.get());
} finally {
    executor.shutdown();
}
A fixed pool bounds concurrency. shutdown lets queued tasks finish; shutdownNow interrupts them.

Sizing and failure modes

  • CPU-bound work: roughly the number of cores, plus a small margin
  • Blocking IO: size from concurrency and latency, not just cores
  • Use a bounded queue; an unbounded queue can hide overload until memory exhausts
  • Choose a rejection policy deliberately: abort, caller-runs, discard or discard-oldest
  • Virtual threads (Java 21) make thread-per-task viable for blocking IO

newCachedThreadPool is unbounded and can create a thread per task, which is dangerous under load. Always shut down executors and await termination during application shutdown, or tasks and threads can outlive the request that created them.

Do not run blocking calls on the common ForkJoinPool. It has a fixed parallelism and is shared by parallel streams and CompletableFuture defaults.

Follow-ups you should expect

Interviewers use these to check whether the first answer was understanding or memorisation.

  1. 1How do core size, max size and the queue interact?
  2. 2Why is an unbounded queue risky?
  3. 3How would you size a pool for blocking IO?
  4. 4What changes with virtual threads?

Say it in the interview

A thread pool reuses worker threads and bounds concurrency. ThreadPoolExecutor uses core threads, a queue, then extra threads up to the maximum, and finally a rejection policy. I size CPU-bound pools near the core count and blocking pools from concurrency and latency. I use bounded queues so overload is visible, shut the pool down at application shutdown, and consider virtual threads for high-concurrency blocking IO.

Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.

Related questions

Reading is step one

Now say it out loud.

Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live Java mock interview and get scored feedback on depth, clarity and confidence.