All topics
library
advanced

Shutdown Hooks & Graceful Termination

Register cleanup logic for JVM shutdown and design applications for graceful termination.

A shutdown hook is a thread the JVM runs on its way out.

Shutdown hook = a 'close out' procedure at a store. When the 'closing time' signal comes (shutdown), the staff (hooks) rush to finish tasks: close the register (flush buffers), lock the doors (close connections), turn off lights (release resources).

Key Concepts

1
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
      log.info("shutting down");
      pool.shutdown();
      dataSource.close();
    }));
2
When they run.
    System.exit(n)
    SIGTERM        the normal docker stop, kubectl delete, Ctrl+C
    the last non-daemon thread finishes
3
When they do not.
    SIGKILL (kill -9)      nothing runs
    a JVM crash
    Runtime.halt()         deliberately skips them
4
So a hook is a best effort. Anything that must survive a hard kill needs to be durable before the kill, not cleaned up after.
5
All hooks start at once, in no defined order, and the JVM waits for all of them. So one hook that blocks forever hangs the shutdown — which is why the orchestrator eventually sends SIGKILL.
6
Graceful shutdown is more than a hook. The pattern is: stop accepting work, finish what is in flight, then exit.
7
    private volatile boolean running = true;
8
    void loop() {
      while (running) { handle(queue.take()); }
    }
9
    // in the hook
    running = false;
    pool.shutdown();                                  // no new tasks
    if (!pool.awaitTermination(30, SECONDS))
      pool.shutdownNow();                             // then interrupt
10
volatile on the flag is required. Without it the loop thread may never see the change, because the JVM is free to keep it in a register.
volatile
11
shutdown versus shutdownNow.
    shutdown()      refuse new tasks, let running ones finish
    shutdownNow()   also interrupt the running ones, return the queued
shutdownshutdownNow
12
Call shutdown, wait, then shutdownNow as a fallback. Calling shutdownNow first discards queued work.
shutdownshutdownNow
13
Daemon threads get no hook of their own. When the last non-daemon thread ends, daemon threads are killed where they stand, mid-write if that is where they are. Use setDaemon(true) for background work that must never block exit, and not for anything holding state.
setDaemon(true)
14
Spring Boot does this already — the context closes, @PreDestroy runs, and the web server drains. A standalone application writes the hook itself.
@PreDestroy
15
What the interviewer is probing.1. "When does a shutdown hook not run?" Probing: the limits. Stalls: "It always runs." Moves up: not on SIGKILL, a JVM crash, or Runtime.halt — so anything that must survive has to be durable before the kill.
16
2. "What order do hooks run in?" Probing: a wrong assumption people build on. Stalls: "Registration order." Moves up: all start concurrently in no defined order, and the JVM waits for all of them, so one that blocks hangs shutdown.
17
3. "Why must the running flag be volatile?" Probing: visibility. Stalls: "It is just a boolean." Moves up: without it the loop thread may never see the change, because the JVM can keep the value in a register.
18
4. "shutdown or shutdownNow?" Probing: the ordering that avoids losing work. Stalls: "shutdownNow, it is faster." Moves up: shutdown, then awaitTermination, then shutdownNow as a fallback — calling shutdownNow first discards everything queued.