Explain awaitTermination, isShutdown, and isTerminated. How do you correctly wait for an ExecutorService to finish?
answer
- isShutdown = requested; isTerminated = actually finished
- isTerminated implies isShutdown, not the reverse
- awaitTermination blocks; returns true=done, false=timed out
- awaitTermination does NOT start shutdown itself
- never busy-poll isTerminated()
basics
~10 sisShutdown() is true once you've called shutdown/shutdownNow. isTerminated() is true only after all tasks have actually finished. awaitTermination() blocks until termination or a timeout, so you call it to wait.
solid answer
~40 sshutdown() and shutdownNow() return immediately, so you need a way to know when work has actually drained. isShutdown() returns true as soon as a shutdown has been *requested* — but tasks may still be running, so it is not 'done'. isTerminated() returns true only after a shutdown was requested AND every task has finished. Both are non-blocking, instant checks. awaitTermination(timeout, unit) is the blocking call: it parks the calling thread until the pool terminates, the timeout expires, or the caller is interrupted, returning true if termination completed within the timeout and false if it timed out. The correct shutdown wait is: call shutdown(), then awaitTermination with a sensible timeout, and escalate to shutdownNow() if it returns false. Polling isTerminated() in a busy loop is wrong — use awaitTermination instead.
go deeper
Knows awaitTermination is how you wait for tasks to finish and that you must call shutdown first.
Distinguishes isShutdown (requested) from isTerminated (finished), knows awaitTermination's boolean return and timeout semantics.
Builds robust shutdown loops with grace-period awaitTermination and escalation; avoids busy-polling and handles the InterruptedException from awaitTermination.
Designs lifecycle hooks (e.g., Spring @PreDestroy, JVM shutdown hooks) that bound total drain time across many pools, and reasons about what to do with work still running when the deadline passes.
## The problem awaitTermination solves When you call `shutdown()` or `shutdownNow()` on an `ExecutorService`, the call **returns immediately** — it only changes the pool's state; the worker threads may still be finishing tasks. So 'I asked it to stop' and 'it has actually stopped' are two different facts, and there are three query/wait methods that distinguish them. ## isShutdown() — has shutdown been *requested*? `boolean isShutdown()` returns `true` the moment **either** `shutdown()` **or** `shutdownNow()` has been called. It says nothing about whether tasks have finished — a pool can be `isShutdown() == true` while threads are still actively running. It is a cheap, non-blocking, instantaneous check. ## isTerminated() — is it *fully done*? `boolean isTerminated()` returns `true` only when **a shutdown was requested AND all tasks have completed** (queue drained and all workers exited). If you never called shutdown, it is always `false`. It is also non-blocking and instantaneous. The relationship is: `isTerminated()` implies `isShutdown()`, but not vice versa. ## awaitTermination(timeout, unit) — *block* until done `boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException` is the blocking counterpart. It parks the calling thread until one of: - the pool **terminates** → returns `true`, - the **timeout elapses** → returns `false`, - the **calling thread is interrupted** → throws `InterruptedException`. Important: `awaitTermination` **does not itself initiate shutdown**. You must call `shutdown()`/`shutdownNow()` first; otherwise `awaitTermination` will simply block until the timeout and return `false`, because the pool will never terminate on its own. ## The correct wait pattern ``` pool.shutdown(); if (!pool.awaitTermination(30, SECONDS)) { pool.shutdownNow(); pool.awaitTermination(10, SECONDS); } ``` Do **not** busy-poll `while (!pool.isTerminated()) {}` — that burns a CPU and is exactly what `awaitTermination` exists to avoid. Use `isTerminated()` only for an occasional non-blocking status check (e.g., a health endpoint), not to wait. ## Summary | Method | Blocks? | True when | |---|---|---| | isShutdown() | No | shutdown OR shutdownNow was called | | isTerminated() | No | shutdown requested AND all tasks done | | awaitTermination(t,u) | Yes (up to t) | returns true if terminated within t, else false |
- If you call awaitTermination without ever calling shutdown(), what happens?It blocks for the full timeout and returns false — the pool never terminates on its own, so awaitTermination can never see termination.
saying these in an interview costs you the question
- Treating isShutdown() as meaning 'all work is done' (that's isTerminated())
- Expecting awaitTermination to trigger shutdown without a prior shutdown() call
- Busy-looping on isTerminated() instead of using awaitTermination
- Ignoring the boolean return of awaitTermination (false = it timed out, work may still be running)