Command Palette

Search for a command to run...

PHASE 5Advanced ~7 min· topic 4 of 11

Topic 5.4

Thread Pools

In one line

ExecutorService, ThreadPoolExecutor, its queue, worker threads and rejection policy — the operational half of concurrency.

0/11 · 0%

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

  1. 01

    ThreadPoolExecutor(core, max, keepAlive, unit, workQueue, handler) — every parameter is a tuning dial.

  2. 02

    Queue choices: unbounded (LinkedBlockingQueue — safe but can OOM), bounded (ArrayBlockingQueue), SynchronousQueue (hand-off).

  3. 03

    Rejection policies: AbortPolicy (default, throws), CallerRunsPolicy (runs on caller — back-pressure), DiscardOldest, Discard.

  4. 04

    core vs max: pool grows from core→max only when the queue fills (bounded queue semantics).

  5. 05

    Spring: @Async with an injected TaskExecutor config is the bean-level equivalent.

  6. 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

inside a ThreadPoolExecutordiagram
Rendering diagram…
ThreadPoolConfig.javajava

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

01

Why is 'unbounded queue + max pool' a memory bomb disguised as design?

Practice

01

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.

Back to phase