Skip to content
Java multithreading 5 min read

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:

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

FeatureUser ThreadDaemon Thread
JVM waits for it?YesNo
Default typeYes (default)No (must set explicitly)
Typical useBusiness logicBackground / support work
Survives main exit?YesNo — JVM exits with main
PriorityAnyUsually 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) before thread.start(). Setting it afterward causes an IllegalThreadStateException and 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 finally block 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:

  1. A thread finishes (calls run() return or throws uncaught exception).
  2. The JVM decrements its live-user-thread counter.
  3. If the counter reaches zero, the JVM initiates shutdown — regardless of how many daemon threads are still alive.
  4. During shutdown, daemon threads receive no InterruptedException or any other signal. Their stacks are simply abandoned and their native resources reclaimed by the OS.

This means:

  • finally blocks 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
  • 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
Last updated June 13, 2026
Was this helpful?