Back to Blog
Java

Java Daemon Thread: Behavior, Creation, and Use Cases

java daemon thread: Learn how Java daemon threads work, how to create them with setDaemon(true), and when background tasks should or should not use them.

daemon threadJVM lifecyclethread lifecyclebackground tasksconcurrency
Illustration of a Java daemon thread running in the background while the JVM main thread completes and exits

A Java daemon thread is a thread that runs in the background and does not prevent the JVM from terminating. When the main thread completes and every remaining thread is a daemon thread, the JVM shuts down immediately. This behavior is controlled by a single boolean flag on the Thread object, set through setDaemon(true).

The distinction matters in any application that starts background work. A non-daemon thread keeps the JVM alive even after main returns. If you start a background task without thinking about its daemon status, the application may stay running long after the main work is done. Conversely, a daemon thread that is still executing when the JVM shuts down is killed mid-operation.

By default, a new thread inherits the daemon status of the thread that created it. The thread that runs main is a user thread, so threads created from the main thread are user threads unless you explicitly call setDaemon(true).

Creating a daemon thread with setDaemon(true)

The Thread class exposes the daemon flag through setDaemon(boolean) and isDaemon(). The flag must be set before the thread starts.

Thread cleanupWorker = new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { // periodic cleanup work } }); cleanupWorker.setDaemon(true); cleanupWorker.start();

Calling setDaemon(true) after start() throws IllegalThreadStateException. This is a common mistake when a thread is created and started in separate code paths, such as a helper method that starts a thread and a later configuration step that tries to mark it as daemon.

For ExecutorService, the equivalent control is a ThreadFactory:

ThreadFactory daemonFactory = runnable -> { Thread thread = new Thread(runnable); thread.setDaemon(true); return thread; }; ExecutorService executor = Executors.newSingleThreadExecutor(daemonFactory);

This matters because Executors default thread factories create non-daemon threads. A thread pool created with the default factory will keep the JVM alive until the pool is explicitly shut down.

How the JVM decides when to exit

The JVM tracks the number of live non-daemon threads. As long as at least one non-daemon thread is alive, the JVM continues running. When the last non-daemon thread terminates, the JVM initiates shutdown. Daemon threads are not given a chance to finish; they are stopped as part of the shutdown sequence.

This means the daemon flag is not a priority or scheduling hint. It only controls whether the thread participates in keeping the JVM alive. A daemon thread can still consume CPU and memory while it runs, and it can block on I/O. The flag does not make the thread cheaper or faster.

The shutdown behavior has a direct consequence: any cleanup that must happen before the process exits cannot rely on daemon threads. If a daemon thread is in the middle of writing a file or flushing a buffer when the last user thread finishes, that work may be lost.

Daemon vs user threads: what changes in practice

PropertyDaemon threadUser thread
Keeps JVM aliveNoYes
finally at shutdownNot guaranteedRuns during normal termination
Default statusInherited from parentInherited from parent
Typical roleBackground servicesPrimary application work

The finally difference is the one that causes the most production surprises. In normal operation, a finally block runs when a thread exits through an exception or a normal return. But during JVM shutdown, daemon threads are killed without unwinding the stack, so finally blocks may not execute. Code that closes sockets, releases locks, or deletes temporary files in a finally block can silently skip that work when the application shuts down.

Common use cases for daemon threads

Daemon threads fit background work that is useful while the application runs but does not need to finish when the application exits.

Typical examples:

  • Metrics collection and logging aggregation that periodically flushes counters
  • Cache eviction that removes expired entries on a schedule
  • Watchdog timers that detect stalled components
  • Connection pool maintenance that reaps idle connections

In each case, the work is continuous and best-effort. If the application shuts down while the daemon thread is between cycles, losing the current cycle is acceptable.

Pitfalls and edge cases

The most dangerous pattern is using a daemon thread for work that must complete. Consider a background thread that writes audit records to a file. If it is a daemon thread and the application exits while the thread is in the middle of a write, the record is lost. The finally block that flushes the writer may never run.

Another edge case is thread pools. A pool created with the default Executors factory uses non-daemon threads. If you submit tasks to that pool and the main thread returns, the JVM stays alive because the pool threads are user threads. This is often the correct behavior for a server, but it can be surprising in a batch job that expects to exit when main returns. Using a daemon ThreadFactory for the pool changes that behavior, but it also means in-flight tasks are abandoned at shutdown.

ThreadLocal values in daemon threads are also worth noting. Since the thread may be killed abruptly, any cleanup that would normally happen in a finally block, such as removing a ThreadLocal value, may not run. If the daemon thread is long-lived and the application does not shut down, this is less of a concern, but it still matters for pooled daemon threads that execute many tasks.

When not to use daemon threads

Use a user thread when the work must complete before the process exits. This includes:

  • Writing data that must be durable, such as audit logs or queued messages
  • Releasing external resources like database connections or file handles
  • Coordinating shutdown with other components through CountDownLatch or join()

A common alternative is to keep the thread non-daemon and signal it to stop explicitly, then join it during shutdown:

Thread worker = new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { // work } }); worker.start(); // during shutdown worker.interrupt(); worker.join(5000);

This gives the thread a chance to run its finally blocks and release resources. The timeout on join prevents the shutdown from hanging indefinitely if the thread does not respond to interruption.

The decision rule is simple: if losing the thread's remaining work at shutdown is acceptable, a daemon thread is fine. If the work must finish, use a user thread and coordinate shutdown explicitly.

java daemon thread: Practical Usage and Code Examples | RYUSLOG DEV