BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ Concurrency

Concurrency

Executors and thread pools

An executor runs tasks on a reusable pool of threads.

Learn these first

At a glance

Mental model

A taxi stand: passengers (tasks) queue up and a fixed fleet of taxis (threads) takes them in turn, instead of buying a new car for each trip.

What happens, in order
  1. 1Executors.newFixedThreadPool(4) creates four worker threadssubmit(task) queues the task and returns a Future; future.get() waits for the result.
  2. 2When all threads are busy, tasks wait in a queueThe size of the pool and the queue determine throughput and memory use.
  3. 3shutdown() stops accepting new tasks and lets queued ones finish; awaitTermination waits for itForgetting to shut down keeps non-daemon threads alive, so the JVM never exits.
  4. 4ScheduledExecutorService.scheduleAtFixedRate runs a task periodically
Remember
Watch out
Never calling shutdownUnbounded queuesIgnoring exceptions inside submitted tasksBlocking on get() inside the same small pool (starvation deadlock)One shared pool for slow and fast tasks
Example (highlighted lines explain it)
ExecutorService pool = Executors.newFixedThreadPool(4);List<Future<Integer>> futures = new ArrayList<>();for (String file : files) {    futures.add(pool.submit(() -> countLines(file)));}int total = 0;for (Future<Integer> f : futures) {    total += f.get();}pool.shutdown();
  1. line 1 uses ExecutorService, the core of Executors and thread pools
  2. line 4 uses submit, the core of Executors and thread pools
  3. line 10 uses shutdown, the core of Executors and thread pools
Read the full explanation (about 1 min)

An executor runs tasks on a reusable pool of threads. You submit work and get a Future; the pool decides which thread runs it and how many run at once.

A mental model

A taxi stand: passengers (tasks) queue up and a fixed fleet of taxis (threads) takes them in turn, instead of buying a new car for each trip.

How it works

Executors.newFixedThreadPool(4) creates four worker threads. submit(task) queues the task and returns a Future; future.get() waits for the result.

When all threads are busy, tasks wait in a queue. The size of the pool and the queue determine throughput and memory use.

shutdown() stops accepting new tasks and lets queued ones finish; awaitTermination waits for it. Forgetting to shut down keeps non-daemon threads alive, so the JVM never exits.

ScheduledExecutorService.scheduleAtFixedRate runs a task periodically.

Going deeper

Size pools by workload: about the number of CPU cores for CPU-bound work, larger for blocking I/O. newFixedThreadPool and newCachedThreadPool use unbounded queue or unbounded threads, which can exhaust memory; build a ThreadPoolExecutor with a bounded queue and a rejection policy for production. Exceptions inside submitted tasks are stored in the Future and vanish unless you call get().

Under the hood

ThreadPoolExecutor keeps core threads, a work queue and a max thread count: it starts a new thread up to core size, then queues, then grows to max, then rejects. Workers loop taking tasks from the queue.

Common mistakes

  • Never calling shutdown.
  • Unbounded queues.
  • Ignoring exceptions inside submitted tasks.
  • Blocking on get() inside the same small pool (starvation deadlock).
  • One shared pool for slow and fast tasks.

How Unravel recognises it

When you paste code, Unravel looks for these signs of executors and thread pools. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Builds on this

Frequently asked questions

What is Executors and thread pools?

An executor runs tasks on a reusable pool of threads. You submit work and get a Future; the pool decides which thread runs it and how many run at once.

What are the most common mistakes with Executors and thread pools?

Never calling shutdown. Unbounded queues. Ignoring exceptions inside submitted tasks. Blocking on get() inside the same small pool (starvation deadlock). One shared pool for slow and fast tasks.

How can I see Executors and thread pools in real code?

Paste your own Java into Unravel and it will point out where executors and thread pools appears, explain how it works at the detail level you choose, and draw the flow of the code around it.