Why your Java program never exits (ExecutorService)

Read the explanation
What if

main submits three tasks and returns without shutdown(). The tasks run, but the pool's threads wait for more work forever.

Predict first

main submits three quick tasks to a fixed pool and returns, without calling shutdown(). What happens?

012345678main: newFixedThreadPool(2)poolmain: submit tasks 1-3submitmain: print, returnreturntask 1task 3waits for more work: forevertask 2waits for more work: forever

Tap any block to see what that thread is doing.

  • Running
  • Waiting / parked
Step 1/4

main makes a pool of two threads and submits three quick tasks.

1ExecutorService pool = Executors.newFixedThreadPool(2);2for (int i = 1; i <= 3; i++) {3    int task = i;4    pool.submit(() -> System.out.println("task " + task + " done"));5}6System.out.println("main returns");7// no shutdown(): the pool's threads wait for more work, and the JVM never exits

Explanation

What just happened

main submitted three tasks and returned. The two pool threads ran them, then went back to waiting for work. They’re normal threads, not daemons, and the JVM only exits when every non-daemon thread has ended. Nobody told the pool there would be no more work, so it never ends.

Try the what-if switch

Which one to use

shutdown() then awaitTermination() for a normal end. shutdownNow() when you’re giving up, like on a timeout. On JDK 19+, try-with-resources does shutdown() and the wait for you.

Watch out

submit() after shutdown() throws RejectedExecutionException. And shutdownNow() only interrupts: a task that never checks for interrupts runs on.

Common questions

Why doesn't my Java program exit after main finishes?

A thread pool is still alive. Executors.newFixedThreadPool makes normal (non-daemon) threads, and after their tasks they wait for more work. The JVM waits for them until you call shutdown().

What's the difference between shutdown() and shutdownNow()?

shutdown() rejects new tasks and lets running and queued ones finish. shutdownNow() interrupts running tasks and returns the queued ones without running them.

Does shutdown() wait for tasks to finish?

No, it returns at once. Call awaitTermination() after it, or use try-with-resources: since JDK 19, close() shuts the pool down and waits for every task.

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