Daemon Threads
Every Java application runs at least one user thread — the main thread. But the JVM also keeps a set of daemon threads quietly humming in the background, doing housekeeping work so your real program can shine. Understanding the difference between daemon and non-daemon (user) threads is important whenever you write long-running background tasks.
What Is a Daemon Thread?
A daemon thread is a low-priority background thread whose sole purpose is to serve user threads. The JVM automatically exits as soon as all user (non-daemon) threads finish — it does not wait for daemon threads to complete.
Classic examples of built-in daemon threads:
- The Garbage Collector thread (see Garbage Collection Deep-Dive)
- The JIT compiler thread (see JIT Compilation & Bytecode)
- Finalization threads
- Timer service threads
Note: If the last user thread finishes, the JVM shuts down even if daemon threads are still running — no cleanup, no finally blocks for those threads.
Daemon vs User Thread
| Feature | User Thread | Daemon Thread |
|---|---|---|
| JVM waits for it? | Yes | No |
| Default type | Yes (default) | No (must set explicitly) |
| Typical use | Business logic | Background / support work |
| Survives main exit? | Yes | No — JVM exits with main |
| Priority | Any | Usually low |
Creating a Daemon Thread
You mark a thread as a daemon by calling setDaemon(true) before you call start(). Calling it after start() throws an IllegalThreadStateException.
public class DaemonExample {
public static void main(String[] args) throws InterruptedException {
Thread daemonThread = new Thread(() -> {
while (true) {
System.out.println("Daemon thread is running...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
daemonThread.setDaemon(true); // must be called before start()
daemonThread.start();
System.out.println("Main thread sleeping for 1.5 seconds...");
Thread.sleep(1500);
System.out.println("Main thread finished.");
// JVM exits here — daemon thread is abruptly stopped
}
}
Output:
Main thread sleeping for 1.5 seconds...
Daemon thread is running...
Daemon thread is running...
Daemon thread is running...
Main thread finished.
The daemon thread printed three times (once every 500 ms), but it never got to clean itself up — the JVM exited as soon as main ended.
Checking Whether a Thread Is a Daemon
Use isDaemon() to check the status of any thread at runtime.
Thread t = new Thread(() -> System.out.println("worker"));
System.out.println("Is daemon before set: " + t.isDaemon()); // false
t.setDaemon(true);
System.out.println("Is daemon after set: " + t.isDaemon()); // true
Thread main = Thread.currentThread();
System.out.println("Main is daemon: " + main.isDaemon()); // false
Output:
Is daemon before set: false
Is daemon after set: true
Main is daemon: false
Daemon Threads Inherit the Daemon Status
When you create a new thread, it inherits the daemon status of its parent thread. If your code spawns a thread from inside a daemon thread, the child is also a daemon — even if you never call setDaemon(true) explicitly.
public class InheritedDaemon {
public static void main(String[] args) {
Thread parent = new Thread(() -> {
Thread child = new Thread(() ->
System.out.println("Child is daemon: " +
Thread.currentThread().isDaemon())
);
System.out.println("Parent is daemon: " +
Thread.currentThread().isDaemon());
child.start();
});
parent.setDaemon(true);
parent.start();
try { parent.join(); } catch (InterruptedException e) { }
}
}
Output:
Parent is daemon: true
Child is daemon: true
Tip: Be careful when using thread pools or frameworks that create threads internally — those threads may end up as daemons without you realizing it.
The Rule: Set Before Start
Trying to change daemon status after a thread has started is an error:
Thread t = new Thread(() -> {});
t.start();
t.setDaemon(true); // throws IllegalThreadStateException
Warning: Always call
setDaemon(true)beforethread.start(). Setting it afterward causes anIllegalThreadStateExceptionand your intended configuration is silently ignored in older code that catches it blindly.
Practical Use Cases
Daemon threads are great for tasks that:
- Must not block JVM shutdown — background logging flushers, metrics reporters
- Are pure support work — cache invalidation, periodic cleanup, heartbeat senders
- Have no critical shutdown requirements — if data loss on JVM exit is acceptable
They are not appropriate when:
- You need to guarantee a file write or database commit completes
- You need a clean
finallyblock or shutdown hook to run (use a Shutdown Hook instead) - You are doing transactional work
Using Daemon Threads with ExecutorService
When you use a Thread Pool, the threads created by the default factory are user threads. To make pool threads daemons, supply a custom ThreadFactory:
import java.util.concurrent.*;
public class DaemonPool {
public static void main(String[] args) {
ThreadFactory daemonFactory = r -> {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("daemon-worker-" + t.getId());
return t;
};
ExecutorService pool = Executors.newFixedThreadPool(2, daemonFactory);
pool.submit(() -> System.out.println(
Thread.currentThread().getName() + " isDaemon: " +
Thread.currentThread().isDaemon()
));
pool.shutdown();
}
}
Output:
daemon-worker-22 isDaemon: true
Under the Hood
When the JVM decides whether to exit, it checks its internal thread registry for live non-daemon threads. The decision loop is roughly:
- A thread finishes (calls
run()return or throws uncaught exception). - The JVM decrements its live-user-thread counter.
- If the counter reaches zero, the JVM initiates shutdown — regardless of how many daemon threads are still alive.
- During shutdown, daemon threads receive no
InterruptedExceptionor any other signal. Their stacks are simply abandoned and their native resources reclaimed by the OS.
This means:
finallyblocks in daemon threads may not execute during JVM shutdown.- Native resources (file handles, sockets) held only by daemon threads may leak momentarily until the OS reclaims them.
- Shutdown hooks registered via
Runtime.getRuntime().addShutdownHook(...)do run on normal JVM exit, giving you a clean alternative to cleanup code in daemon threads.
The Thread Life Cycle applies equally to daemon threads — they go through NEW → RUNNABLE → BLOCKED/WAITING → TERMINATED states just like user threads. The only difference is that the JVM doesn’t wait for them to reach TERMINATED before exiting.
Note: In Java 21, Virtual Threads created with
Thread.ofVirtual().start(...)are daemon threads by default. This is intentional — virtual threads are designed for short-lived tasks and should not hold up JVM shutdown.
Quick Reference
// Create and configure a daemon thread
Thread t = new Thread(myRunnable);
t.setDaemon(true); // mark as daemon — BEFORE start()
t.start();
// Check status
boolean d = t.isDaemon(); // true
// Inherited by child threads spawned from a daemon thread
// Always call setDaemon() before start() — IllegalThreadStateException otherwise
Related Topics
- Thread Life Cycle — understand the states a thread (including daemon threads) moves through
- Thread Pool — how to provide a custom ThreadFactory that marks pool threads as daemons
- Shutdown Hook — the correct way to run cleanup code when the JVM exits
- Virtual Threads — Java 21 virtual threads are daemons by default; learn why
- Garbage Collection Deep-Dive — the GC thread is the most famous daemon thread in the JVM
- Create a Thread — the basics of creating and starting threads in Java