How a thread pool runs tasks

Read the explanation
What if

A fixed pool runs as many tasks as it has threads. The rest wait in a queue for the next free thread.

012345678submit ×4shutdown()awaitTermination()continuestask 1task 3task 2task 4

Tap any block to see what that thread is doing.

  • Running
  • Waiting / parked
Step 1/5

main submits 4 tasks to a pool of 2 threads. submit() returns at once.

1ExecutorService pool = Executors.newFixedThreadPool(2);2for (int i = 1; i <= 4; i++) {3    int id = i;4    pool.submit(() -> work("task " + id));5}6pool.shutdown();7pool.awaitTermination(1, TimeUnit.MINUTES);

Explanation

What just happened

The pool had 2 threads and got 4 tasks. Two tasks ran right away. The other two waited in the pool’s queue, and each was picked up by whichever thread finished first. No new threads were created. The same two threads did all the work.

Try the what-if switch

4 threads: the same four tasks each get a thread at once and nothing queues. It all finishes when the slowest task does, at 5 instead of 7. Past the number of tasks (or cores, for CPU work), more threads stop helping.

Why pools exist

Starting a platform thread is expensive: it costs memory and an OS call. A pool creates a few threads once and keeps reusing them. It also caps how much runs at once, so 10,000 requests don’t become 10,000 threads.

Choosing a pool size

Watch out

newFixedThreadPool uses an unbounded queue. If tasks arrive faster than they finish, the queue grows until you run out of memory. For production, build a ThreadPoolExecutor with a bounded queue and decide what happens when it’s full.

Always shutdown() a pool you created. Its threads aren’t daemons, so they can keep the JVM alive.

Common questions

How does a fixed thread pool run more tasks than it has threads?

Tasks beyond the thread count wait in the pool's queue. Each thread takes the next task when it finishes one, so the same few threads do all the work.

Is the queue of newFixedThreadPool bounded?

No. Executors.newFixedThreadPool uses an unbounded LinkedBlockingQueue. If tasks arrive faster than they finish, the queue grows until memory runs out. Build a ThreadPoolExecutor with a bounded queue for production.

Do I need to shut down an ExecutorService?

Yes. The pool's threads are not daemon threads, so a pool you never shutdown() can keep the JVM alive.

Verified: this timeline matches a real program, checked in CI on JDK 21 and 25. How we verify. Unclear or wrong? Tell us