In Java, threads are classified into two categories: user threads and daemon threads.
While user threads are designed to perform tasks that need to be completed before the program exits, daemon threads are background threads that provide support to other user threads, and their life cycle is tied to the life of user threads. Once all user threads are finished, the JVM automatically terminates all daemon threads.
Daemon threads are typically used for background tasks like garbage collection, monitoring services, and other tasks that do not require the program to wait for their completion.
In this tutorial, we will cover:
The concept of daemon threads and their lifecycle.
How to create and use daemon threads in Java.
Code examples demonstrating common daemon thread use cases.
1. What is a Daemon Thread?
A daemon thread is a low-priority thread that runs in the background, performing support tasks such as resource cleanup. Daemon threads are automatically terminated when all user threads (non-daemon threads) finish their execution.
Key characteristics of daemon threads:
Background nature: Daemon threads run in the background and do not prevent the JVM from exiting.
JVM termination: When only daemon threads are left running, the JVM exits and does not wait for them to finish.
Use cases: Typically used for background tasks such as monitoring, logging, and garbage collection.
2. How to Create a Daemon Thread
In Java, any thread can be made a daemon thread by calling setDaemon(true) before the thread is started. The isDaemon() method can be used to check if a thread is a daemon thread.
Steps to create a daemon thread:
Create a thread using the Thread class or by implementing the Runnable interface.
Before starting the thread, call the setDaemon(true) method to mark the thread as a daemon.
Start the thread using the start() method.
3. Daemon Thread Example:
Example 1: Simple Daemon Thread
public class SimpleDaemonThreadExample { public static void main(String[] args) { // Creating a thread Thread daemonThread = new Thread(new DaemonTask()); // Setting the thread as a daemon thread daemonThread.setDaemon(true); // Starting the daemon thread daemonThread.start(); // Main thread sleeps for 2 seconds and then terminates try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } // When the main thread exits, the JVM terminates the daemon thread System.out.println("Main thread finished."); } } class DaemonTask implements Runnable { @Override public void run() { while (true) { System.out.println("Daemon thread is running..."); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
Explanation:
The thread running the DaemonTask is marked as a daemon using setDaemon(true).
The main thread sleeps for 2 seconds and then terminates.
When the main thread exits, the daemon thread is automatically terminated, even though it is running an infinite loop.
Example 2: Mixing User and Daemon Threads
In this example, we create both user and daemon threads to observe how the JVM behaves when the user threads complete.
public class UserAndDaemonThreadExample { public static void main(String[] args) { // User thread Thread userThread = new Thread(new UserTask()); userThread.setDaemon(false); // Explicitly set as a user thread (optional, as it's false by default) // Daemon thread Thread daemonThread = new Thread(new DaemonTask()); daemonThread.setDaemon(true); // Set as a daemon thread // Starting both threads userThread.start(); daemonThread.start(); // Main thread will wait for the user thread to finish try { userThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Main thread finished. JVM will exit."); } } class UserTask implements Runnable { @Override public void run() { for (int i = 1; i <= 5; i++) { System.out.println("User thread iteration " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("User thread finished."); } } class DaemonTask implements Runnable { @Override public void run() { while (true) { System.out.println("Daemon thread is running..."); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
Explanation:
User thread (UserTask): Runs for 5 iterations, with a 1-second sleep between iterations.
Daemon thread (DaemonTask): Runs an infinite loop, but it will be terminated automatically once the user thread completes.
The userThread.join() ensures that the main thread waits for the user thread to finish before terminating.
The daemon thread continues running until the user thread finishes, at which point the JVM shuts down.
4. Important Methods for Daemon Threads
Here are some important methods related to daemon threads:
setDaemon(boolean on): Sets the thread as a daemon thread if on is true. This method must be called before starting the thread.
isDaemon(): Returns true if the thread is a daemon thread, false otherwise.
Example 3: Checking if a Thread is a Daemon Thread
public class DaemonThreadCheckExample { public static void main(String[] args) { // Creating a daemon thread Thread daemonThread = new Thread(new DaemonTask()); daemonThread.setDaemon(true); // Checking if the thread is a daemon thread System.out.println("Is daemonThread a daemon? " + daemonThread.isDaemon()); // true // Creating a user thread Thread userThread = new Thread(new UserTask()); // Checking if the thread is a user thread System.out.println("Is userThread a daemon? " + userThread.isDaemon()); // false daemonThread.start(); userThread.start(); } } class DaemonTask implements Runnable { @Override public void run() { while (true) { System.out.println("Daemon thread is running..."); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } class UserTask implements Runnable { @Override public void run() { System.out.println("User thread is running."); } }
Explanation:
The isDaemon() method is used to check whether a thread is a daemon or not.
In this example, daemonThread is a daemon thread (true), while userThread is a normal user thread (false).
5. Use Cases for Daemon Threads
Daemon threads are suitable for tasks that do not require the program to wait for their completion. Some common use cases include:
Background Monitoring: Daemon threads can be used for monitoring services, such as logging, system health monitoring, or performance tracking.
Garbage Collection: The JVMโs garbage collector is a daemon thread that automatically runs in the background.
Resource Cleanup: Daemon threads can handle tasks like temporary file deletion, memory cleanup, and other maintenance tasks.
Example 4: Daemon Thread for Logging
public class LoggingDaemonThreadExample { public static void main(String[] args) { // Creating and starting the logging daemon thread Thread loggingThread = new Thread(new LoggingTask()); loggingThread.setDaemon(true); // Set the thread as a daemon thread loggingThread.start(); // Simulate main application work for (int i = 1; i <= 3; i++) { System.out.println("Main application working on task " + i); try { Thread.sleep(1000); // Simulating work } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Main application finished."); } } class LoggingTask implements Runnable { @Override public void run() { while (true) { System.out.println("Logging system is monitoring..."); try { Thread.sleep(2000); // Simulate background logging work } catch (InterruptedException e) { e.printStackTrace(); } } } }
Explanation:
The LoggingTask simulates a background logging service that runs periodically.
Since it's marked as a daemon thread, it will terminate once the main application finishes, even if it's still in its loop.
6. Key Considerations for Daemon Threads
Daemon threads do not block JVM termination: The JVM does not wait for daemon threads to complete before exiting. Once all user threads finish, daemon threads are terminated without completing their tasks.
Use daemon threads for background tasks: Daemon threads are ideal for tasks that are not critical to program completion, such as logging, monitoring, and periodic cleanups.
Daemon threads must be set before starting: The setDaemon(true) method must be called before the thread is started. Otherwise, it will throw an IllegalThreadStateException.
Conclusion
In this tutorial, we explored daemon threads in Java, covering:
The concept of daemon threads and how they differ from user threads.
How to create and use daemon threads with setDaemon(true).
Examples that demonstrate how daemon threads work in the context of user threads and JVM termination.
Typical use cases, such as background monitoring, logging, and cleanup tasks.
Daemon threads provide a useful mechanism for handling background tasks that do not need to block the termination of the JVM.