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
- CPU-heavy work: about as many threads as cores (
Runtime.getRuntime().availableProcessors()). More threads just take turns on the same cores. - Work that waits on I/O: more threads help, because most of them are waiting. On JDK 21+, virtual threads are often the simpler answer (see the last scenario).
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