What is the difference between shutdown() and shutdownNow() on an ExecutorService, and what does each return or do to in-flight tasks?
answer
- shutdown = graceful drain; shutdownNow = interrupt + return pending
- shutdownNow returns List<Runnable> of never-started tasks
- interrupt is cooperative — CPU loops ignore it
- neither blocks; awaitTermination does the waiting
- two-phase: shutdown → await → shutdownNow → await
basics
~10 sshutdown() stops accepting new tasks but lets already-submitted ones finish. shutdownNow() tries to stop running tasks by interrupting them and returns the list of tasks that never started.
solid answer
~40 sBoth initiate shutdown so no new tasks are accepted, but they differ in how they treat work already in the pool. shutdown() is graceful: it returns void, lets all queued and running tasks complete, then terminates. shutdownNow() is forceful: it drains the queue and returns the List<Runnable> of tasks that were waiting but never started, and it interrupts the threads running active tasks. Crucially, shutdownNow() only *requests* cancellation via Thread.interrupt() — tasks that never check the interrupt flag or call a blocking method that responds to it will keep running. Neither call blocks waiting for completion; you call awaitTermination(timeout) afterward to wait. A robust pattern is shutdown(), await a grace period, then shutdownNow() if it times out, then await again.
code
java · 15 linesvoid shutdownGracefully(ExecutorService pool) {
pool.shutdown(); // stop accepting; let current work finish
try {
if (!pool.awaitTermination(30, TimeUnit.SECONDS)) {
List<Runnable> dropped = pool.shutdownNow(); // interrupt + reclaim pending
log.warn("Forced shutdown; {} tasks never started", dropped.size());
if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {
log.error("Pool did not terminate");
}
}
} catch (InterruptedException e) {
pool.shutdownNow();
Thread.currentThread().interrupt(); // restore the flag
}
}go deeper
Knows shutdown() stops accepting new work and lets current work finish, while shutdownNow() tries to stop things immediately. Can call shutdown() to let the JVM exit.
Articulates the exact contract: queued vs running task handling, the List<Runnable> return, that neither blocks, and that shutdownNow only interrupts.
Explains the cooperative-interruption caveat and the canonical two-phase shutdown-then-shutdownNow pattern with awaitTermination; knows RejectedExecutionException on post-shutdown submit.
Reasons about shutdown semantics across an application's lifecycle — ordering pool shutdowns by dependency, draining vs dropping in-flight work, persisting returned pending tasks, and ensuring tasks are written to honor interruption.
## What an ExecutorService is An **ExecutorService** is Java's abstraction for a pool of worker threads that run submitted tasks (`Runnable`/`Callable`). You hand it work via `submit`/`execute`; internally it keeps a set of worker threads and a **task queue** (a `BlockingQueue`) of work waiting for a free thread. Because it owns live threads, you must explicitly shut it down — otherwise those (often non-daemon) threads keep the JVM alive forever. ## The two shutdown methods **`shutdown()`** — an *orderly*, graceful shutdown. After you call it: - The pool **stops accepting new tasks**: any further `submit`/`execute` throws `RejectedExecutionException`. - All tasks **already in the queue** and all **currently running** tasks are allowed to finish normally. - It returns **`void`** and **does not block** — it just flips the pool into the SHUTDOWN state and returns immediately. **`shutdownNow()`** — a *forceful* shutdown attempt. After you call it: - It also stops accepting new tasks. - It **drains the work queue** and **returns** the `List<Runnable>` of tasks that were waiting but had **not yet started**. You can log, persist, or resubmit them elsewhere. - For tasks that **are already running**, it calls **`Thread.interrupt()`** on each worker thread to *request* cancellation. - It also does **not block**. ## The critical caveat: interrupt is cooperative `shutdownNow()` cannot force a thread to stop. Java has **no safe way to forcibly kill a thread** (`Thread.stop()` is deprecated and dangerous). Interruption is **cooperative**: setting a thread's interrupt flag only has an effect if the task *cooperates* by either checking `Thread.currentThread().isInterrupted()` periodically, or calling a blocking method (`sleep`, `wait`, `join`, `BlockingQueue.take`, many NIO ops) that throws `InterruptedException` in response. A pure CPU loop like `while (true) { compute(); }` that never checks the flag will **ignore `shutdownNow()` entirely** and run forever. ## How they fit the lifecycle Neither method waits for completion. To actually wait you call **`awaitTermination(timeout, unit)`**, which blocks until the pool is terminated, the timeout elapses, or the calling thread is interrupted. The recommended two-phase pattern (from `ExecutorService`'s own Javadoc) is: call `shutdown()`, `awaitTermination` for a grace period, and if it returns `false` call `shutdownNow()` and `awaitTermination` again. ## Summary table | Aspect | shutdown() | shutdownNow() | |---|---|---| | New tasks | Rejected | Rejected | | Queued (not started) tasks | Run to completion | Returned as List<Runnable>, not run | | Running tasks | Run to completion | Interrupted (cooperative) | | Return value | void | List<Runnable> | | Blocks? | No | No |
- What happens if you submit a task after calling shutdown()?The default policy rejects it: submit/execute throws RejectedExecutionException (handled by the pool's RejectedExecutionHandler, AbortPolicy by default).
- Why can't shutdownNow() guarantee tasks stop?Because Java has no safe forced thread-kill; interruption is cooperative. A task must check the interrupt flag or call an interrupt-responsive blocking method to actually stop.
saying these in an interview costs you the question
- Claiming shutdownNow() forcibly kills or stops running threads — it only interrupts them
- Thinking shutdown()/shutdownNow() block until tasks finish (they return immediately)
- Saying shutdown() discards queued tasks (it runs them; shutdownNow() returns them)
- Believing a tight CPU loop will stop on shutdownNow()