Daemon threads in Java
Daemon threads don't keep the JVM alive.
What keeps the JVM alive
The JVM exits when all non-daemon (user) threads have finished. Daemon threads are background helpers that don't count: when the last user thread ends, the JVM shuts down and simply abandons the daemons, wherever they are.
Your turn
A daemon thread creates a child thread. Is the child a daemon?
Thread parent = new Thread(() -> {
Thread child = new Thread(() -> {});
System.out.println(child.isDaemon());
});
parent.setDaemon(true);
parent.start();
parent.join();truefalseThrows IllegalThreadStateException
Show the answer
true. A new platform thread inherits daemon status from the thread that creates it. main is a user thread, so threads made by main start as non-daemon (isDaemon() is false until you call setDaemon(true)). This child was made by a daemon.
Decide before start()
setDaemon(true) must be called before start(). Calling it on a thread that is already alive throws IllegalThreadStateException.
Thread flusher = new Thread(this::flushLogs);
flusher.setDaemon(true); // before start!
flusher.start();finally isn't guaranteed
A CLI starts a daemon that flushes logs every second; main prints its result and returns. The last log lines go missing: the JVM exits and stops the daemon mid-flush. A daemon's finally blocks may never run. If work must complete, have main join() the thread, or make it a non-daemon thread.
The forgotten pool
You create Executors.newFixedThreadPool(4), run a few tasks, and forget shutdown(). main ends. Does the JVM exit?
Think about it, then reveal the answer
No. Pool workers are non-daemon by default, so even idle workers keep the JVM alive. That's why a forgotten pool makes a program hang at the end. Contrast: virtual threads are always daemon, so one blocked on a socket read won't keep the JVM running.
Choosing wisely
Use daemon threads only for work that's safe to drop: refreshing a cache, sampling metrics, a heartbeat. Anything that must finish, like writing a report or flushing data, belongs on a user thread that you join() or shut down properly. Mixing these up gives you either hanging tools or lost data.
Key takeaways
- The JVM exits when only daemon threads remain
- setDaemon() must be called before start()
- Daemons can stop abruptly — finally blocks aren't guaranteed
- New threads inherit daemon status; virtual threads are always daemon
Virtual threads can't be anything but daemons: calling setDaemon(false) on one throws IllegalArgumentException.
Practice questions
What does this print?
Thread t = new Thread(() -> {});
System.out.println(t.isDaemon());
t.setDaemon(true);
System.out.println(t.isDaemon());- false true
- true true
- false false
- true false
Check your answer
false true. A new platform thread inherits daemon status from the thread that creates it. main is a user thread, so t starts as non-daemon until setDaemon(true).
A CLI tool starts a daemon thread that flushes logs to disk every second. main returns right after printing its result. The last log lines are often missing. Why?
- The JVM exits once main ends, abandoning the daemon mid-flush
- Daemon threads get no CPU while main is running
- Background threads can't write files
- The garbage collector clears pending log buffers
Check your answer
The JVM exits once main ends, abandoning the daemon mid-flush. Only user threads keep the JVM alive. When main finishes, the JVM shuts down and simply stops the daemon thread, wherever it is.