00:00

Daemon Thread in Java – Background Threads Explained

A daemon thread is a low-importance background thread that does not prevent the JVM from exiting. When all user (non-daemon) threads have finished, the JVM shuts down and simply abandons any daemon threads still running. The garbage collector and other JVM housekeeping threads are daemon threads.

This tutorial explains user vs daemon threads, how to create a daemon with setDaemon(true), the rules involved, and the important risk that a daemon thread can be stopped mid-task without running its finally blocks.

1. User Threads vs Daemon Threads

AspectUser threadDaemon thread
Keeps the JVM aliveYes: the JVM waits for itNo: abandoned at exit
Typical workThe application's real tasksBackground services: monitoring, cache cleanup, heartbeats
Default formain and threads created by user threadsThreads created by daemon threads; JVM internal threads; virtual threads
Clean shutdownFinishes normallyMay stop abruptly, skipping finally blocks

2. Creating a Daemon Thread

Call setDaemon(true) before start(). This heartbeat thread loops forever, yet the program ends as soon as main finishes:

DaemonDemo.java
public class DaemonDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread heartbeat = new Thread(() -> {
            int beat = 0;
            while (true) {                                  // never ends on its own
                System.out.println("heartbeat " + (++beat));
                try { Thread.sleep(100); } catch (InterruptedException e) { return; }
            }
        }, "heartbeat");
        heartbeat.setDaemon(true);                          // must be set BEFORE start()
        heartbeat.start();

        Thread.sleep(350);                                  // main does its work
        System.out.println("main finished; JVM exits and the daemon is stopped");
    }
}
Output (the number of heartbeats may vary slightly)
heartbeat 1
heartbeat 2
heartbeat 3
heartbeat 4
main finished; JVM exits and the daemon is stopped

Without setDaemon(true), the heartbeat thread would keep the program running forever.

3. User Threads Keep the JVM Alive

UserThreadKeepsAlive.java
public class UserThreadKeepsAlive {
    public static void main(String[] args) {
        Thread user = new Thread(() -> {
            try { Thread.sleep(300); } catch (InterruptedException e) { }
            System.out.println("user thread: finished its work, now the JVM may exit");
        });
        user.start();                                       // non-daemon (user) thread
        System.out.println("main: returning from main()");
        // The JVM waits for the user thread before exiting
    }
}
Output
main: returning from main()
user thread: finished its work, now the JVM may exit

Even though main() returned first, the JVM waited for the user thread to finish.

4. Rules of Daemon Threads

DaemonRules.java
public class DaemonRules {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("main is daemon? " + Thread.currentThread().isDaemon());

        Thread daemon = new Thread(() -> {
            Thread child = new Thread(() -> { });
            System.out.println("child of daemon is daemon? " + child.isDaemon());   // inherited
        });
        daemon.setDaemon(true);
        daemon.start();
        daemon.join();

        Thread running = new Thread(() -> {
            try { Thread.sleep(200); } catch (InterruptedException e) { }
        });
        running.start();
        try {
            running.setDaemon(true);                        // too late
        } catch (IllegalThreadStateException e) {
            System.out.println("setDaemon after start(): " + e.getClass().getSimpleName());
        }
        running.join();

        Thread v = Thread.ofVirtual().unstarted(() -> { });
        System.out.println("virtual thread is daemon? " + v.isDaemon());
    }
}
Output
main is daemon? false
child of daemon is daemon? true
setDaemon after start(): IllegalThreadStateException
virtual thread is daemon? true
  • setDaemon must be called before start(); afterwards it throws IllegalThreadStateException.
  • A new thread inherits the daemon status of the thread that creates it.
  • The main thread is a user thread.
  • Virtual threads are always daemon threads; their daemon status cannot be changed to false.
  • isDaemon() reports a thread's status.

5. The Risk: Work Can Be Cut Off

When the JVM exits, daemon threads are stopped wherever they are. Their finally blocks do not run:

DaemonFinallyLost.java
public class DaemonFinallyLost {
    public static void main(String[] args) throws InterruptedException {
        Thread writer = new Thread(() -> {
            try {
                System.out.println("daemon: writing a file...");
                Thread.sleep(5_000);
                System.out.println("daemon: done");               // never printed
            } catch (InterruptedException e) {
            } finally {
                System.out.println("daemon: finally - closing file"); // never printed either!
            }
        });
        writer.setDaemon(true);
        writer.start();
        Thread.sleep(200);
        System.out.println("main: exiting without waiting");
    }
}
Output
daemon: writing a file...
main: exiting without waiting

Neither "done" nor the finally message appeared. Never use daemon threads for work that must complete or clean up, such as writing files, committing transactions or sending messages. Use user threads (or an executor you shut down gracefully) for those.

6. Good Use Cases

  • Periodic monitoring, metrics collection and heartbeats.
  • Cleaning expired cache entries.
  • Background prefetching or warm-up that can safely be abandoned.
  • Timers: new Timer(true) creates a daemon timer thread.

For executor-based daemon threads, supply a ThreadFactory such as Thread.ofPlatform().daemon(true).factory().

7. Summary

  • Daemon threads are background threads that do not keep the JVM running.
  • Mark a thread as daemon with setDaemon(true) before starting it.
  • Daemon status is inherited; main is a user thread; virtual threads are always daemon.
  • Daemon threads can be stopped abruptly without running finally, so use them only for expendable work.

Related Tutorials