Definition
- Daemon thread – a background thread that does not prevent JVM exit; set via
thread.setDaemon(true)beforestart(). - Non-daemon (user) thread – a foreground thread that keeps the JVM alive as long as it is running.
- JVM exit rule – the JVM terminates when all non-daemon threads finish, killing all daemon threads immediately.
- Inheritance – a new thread inherits the daemon status of its parent thread.
- IllegalThreadStateException – thrown if
setDaemon()is called afterstart(). - Finally blocks – may not execute in daemon threads during JVM shutdown, so critical cleanup should use shutdown hooks instead.
At National Bank of India, non-daemon threads are the bank tellers serving customers. The bank stays open as long as at least one teller is working. Daemon threads are the cleaning crew working in the background: sweeping floors, restocking forms, refreshing water coolers. When the last teller finishes and goes home, the bank closes immediately. The cleaning crew is kicked out mid-sweep with no chance to finish. Use daemons for background tasks that are safe to abandon.
Understanding the Concepts
What is a daemon thread?
A daemon thread is a background service thread that the JVM does not wait for. When all non-daemon threads have terminated, the JVM exits, killing all daemon threads abruptly. Daemon threads are used for housekeeping tasks that should stop automatically when the application shuts down.
package com.bank.service;
public class DaemonThreadDemo {
public static void main(String[] args) throws InterruptedException {
Thread cacheRefresher = new Thread(() -> {
while (true) {
try {
System.out.println("Refreshing exchange rate cache...");
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}, "cache-refresher");
cacheRefresher.setDaemon(true); // MUST be before start()
cacheRefresher.start();
Thread.sleep(2500); // main thread sleeps 2.5 seconds
System.out.println("Main thread done. JVM will exit.");
// cache-refresher is daemon: JVM exits without waiting for it
}
}
Refreshing exchange rate cache...
Refreshing exchange rate cache...
Refreshing exchange rate cache...
Main thread done. JVM will exit.
How is a daemon thread different from a user thread?
| Feature | Daemon Thread | User (Non-Daemon) Thread |
|---|---|---|
| Prevents JVM exit | No | Yes |
| Default | Inherits from parent | main thread is non-daemon |
| Finally blocks on shutdown | May not execute | Execute normally |
| Use case | Background housekeeping | Critical business logic |
| Created by | setDaemon(true) before start() | Default (no call needed) |
What are the rules for daemon threads?
There are four rules to remember:
- Call
setDaemon(true)beforestart(). Calling it after throwsIllegalThreadStateException. - Child threads inherit the daemon status of their parent.
- The main thread is non-daemon.
- The JVM exits when all non-daemon threads terminate.
// Rule 1: setDaemon after start throws exception
Thread t = new Thread(() -> {});
t.start();
try {
t.setDaemon(true); // IllegalThreadStateException!
} catch (IllegalThreadStateException e) {
System.out.println("Cannot change daemon status after start");
}
// Rule 2: child inherits parent's daemon status
Thread parent = new Thread(() -> {
Thread child = new Thread(() -> {});
System.out.println("Child daemon: " + child.isDaemon()); // same as parent
});
parent.setDaemon(true);
parent.start();
How do you create a daemon thread pool?
Use a custom ThreadFactory that sets daemon status on every worker thread.
package com.bank.service;
import java.util.concurrent.*;
public class DaemonPoolDemo {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(4, r -> {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("daemon-worker-" + t.getId());
return t;
});
pool.submit(() -> System.out.println(
Thread.currentThread().getName() + " processing"));
// Pool threads are daemon: JVM exits when main finishes
// Good for optional background processing
// Bad for critical work that MUST complete
}
}
Why are finally blocks not guaranteed in daemon threads?
When the JVM exits because all non-daemon threads are done, daemon threads are killed immediately. The JVM does not run their finally blocks or shutdown hooks attached to those threads.
Thread daemon = new Thread(() -> {
try {
Thread.sleep(Long.MAX_VALUE);
} finally {
// MAY NOT EXECUTE if JVM exits!
System.out.println("Saving critical data...");
}
});
daemon.setDaemon(true);
daemon.start();
// If all user threads end, JVM exits, finally is SKIPPED
For critical cleanup, use Runtime.addShutdownHook() instead.
When should you use daemon threads?
- Use daemon for background tasks where abrupt termination is acceptable:
- Cache refresh and warming
- Heartbeat monitoring
- Log rotation and periodic flushing
- Metrics collection
- Background cleanup
- Use non-daemon for tasks that must complete:
- Trade settlement and payment processing
- Database writes and data migration
- File generation and report creation
What about the garbage collector?
The garbage collector runs as a daemon thread. It stops automatically when the JVM exits. This is why GC does not prevent JVM shutdown.
What are common mistakes with daemon threads?
// WRONG: critical work on daemon thread
Thread t = new Thread(() -> settleAllPendingTrades());
t.setDaemon(true); // trades may be lost on JVM exit!
t.start();
// WRONG: relying on finally in daemon for cleanup
Thread t2 = new Thread(() -> {
try { process(); }
finally { flushAuditLog(); } // may not run!
});
t2.setDaemon(true);
// CORRECT: use shutdown hook for critical cleanup
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
flushAuditLog(); // runs on SIGTERM, SIGINT
}));
What are common interview follow-up questions?
- Can a daemon thread create a non-daemon thread? Yes, by explicitly calling setDaemon(false) on the child before start().
- What happens to daemon threads during System.exit()? The JVM runs shutdown hooks, then terminates daemon threads.
- Is the main thread a daemon? No, the main thread is always non-daemon.
Visual Overview
JVM Exit Decision
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ User Thread │ │ User Thread │ │ Daemon │
│ (main) │ │ (worker-1) │ │ (GC, cache) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
▼ ▼ │
main() ends worker finishes │
│ │ │
▼ ▼ │
┌──────────────────────────────────┐ │
│ All user threads finished? │ │
│ YES → JVM exits │─────────────┘
│ Daemon threads KILLED │ (abrupt, no
│ finally blocks SKIPPED │ cleanup)
└──────────────────────────────────┘
Key Takeaway
A daemon thread is a background service thread that the JVM does not wait for. When all non-daemon (user) threads terminate, the JVM exits immediately, killing all daemon threads without running their finally blocks. The garbage collector, JMX threads, and other JVM internal services are daemon threads.
The rules are straightforward. Call setDaemon(true) before start(). Child threads inherit their parent's daemon status. The main thread is non-daemon. The JVM exit condition is simple: no more non-daemon threads means shutdown.
Use daemon threads for background housekeeping that is safe to abandon: cache refresh, heartbeat monitoring, metrics collection. Use non-daemon threads for critical business logic that must complete: trade settlement, payment processing, data writes. Never put critical cleanup in a finally block inside a daemon thread, because JVM exit can skip it. Use Runtime.addShutdownHook() for critical shutdown logic instead.
At National Bank of India, the cache refresher and heartbeat monitor run as daemon threads. They stop automatically when the trading service shuts down. The log flusher was initially a daemon, but it lost the last five seconds of logs on shutdown. Moving the critical flush to a shutdown hook fixed the problem.
Remember one sentence: daemon threads die when the last user thread exits, so never run critical work on them.
---