Turning an ExecutorService to daemon in Java
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
By default, Java's ExecutorService creates non-daemon (user) threads, which prevent the JVM from exiting even when the main method completes. To make the JVM exit when only executor threads remain, create the ExecutorService with a custom ThreadFactory that sets thread.setDaemon(true). Daemon threads are automatically terminated when all user threads finish. This is useful for background tasks like periodic cleanup, logging, and monitoring that should not keep the application alive.
The Problem
Without daemon threads, the JVM stays running indefinitely because the executor's threads are non-daemon by default.
Custom ThreadFactory with Daemon Threads
The lambda r -> { Thread t = new Thread(r); t.setDaemon(true); return t; } is a concise ThreadFactory implementation.
Reusable DaemonThreadFactory
Named threads make debugging easier — stack traces show bg-worker-1 instead of Thread-47.
Using Guava's ThreadFactoryBuilder
ScheduledExecutorService with Daemon Threads
Java 21+ Virtual Threads
Java 21's virtual threads are daemon threads by default:
Graceful Shutdown (Best Practice)
Even with daemon threads, prefer explicit shutdown for clean resource release:
Common Pitfalls
- Daemon threads being killed mid-task: The JVM kills daemon threads abruptly when all user threads finish. If a daemon thread is writing to a file or database, data may be lost or corrupted. Use shutdown hooks or
executor.shutdown()for tasks that must complete. - Not calling
executor.shutdown()on non-daemon executors: If you decide against daemon threads, you must callshutdown()explicitly. Otherwise, the JVM never exits because the executor's threads keep running. - Using daemon threads for critical tasks: Daemon threads are for fire-and-forget background work. Tasks that must run to completion (transaction processing, data persistence) should use non-daemon threads with explicit shutdown management.
- Forgetting to name daemon threads: Unnamed threads appear as
Thread-0,Thread-1in stack traces, making debugging difficult. Always set a meaningful name in theThreadFactory. - Mixing daemon and non-daemon threads in the same pool: An executor pool should be consistently daemon or non-daemon. Mixing causes confusing behavior where some tasks keep the JVM alive and others do not. Use separate executors for daemon and non-daemon workloads.
Summary
- Create a
ThreadFactorythat callsthread.setDaemon(true)and pass it toExecutors.newFixedThreadPool() - Daemon threads are killed when all user threads finish — the JVM does not wait for them
- Use daemon threads for background tasks (monitoring, cleanup, logging) that should not block JVM exit
- Use Guava's
ThreadFactoryBuilderfor a fluent API with naming, daemon, and exception handling - Java 21 virtual threads are daemon by default — no custom factory needed
- Always prefer explicit
shutdown()for clean resource release, even with daemon threads
Related reading
- Twisted Python - Two looping calls, one not firing according to given interval
- two distributed rendering contexts - synchronization
- Two instances of application connected to same, altered database
- TypeError can't pickle _thread.lock objects in Seq2Seq
- Tutorials on Core netty and Protobuf
- Type List vs type ArrayList in Java
- TypeError can't pickle _thread.RLock objects
- Typescript - Wait for promise resolve before function return

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.