Threads in Java, are a light-weight process within a process that helps in the concurrent execution of multiple parts of a program for maximum utilization of CPU. The focus of this article will be on Daemon Threads.

Java offers 2 types of threads:

User Threads

User threads are high priority threads. The application holds the logic to create user threads and these threads are to perform specific tasks in the application. JVM will wait for the completion of the thread, once all the user threads have completed execution, only then the JVM will exit. In simple terms, user threads run in the foreground.

Daemon Threads

Daemon threads in Java are low priority threads. They should be ideally used to support the user threads. JVM creates most of the daemon threads. They are called low priority threads because as soon as all the user threads finish their execution, the JVM will exit. It will not wait for Daemon threads to finish their execution. However, a poorly designed code inside the daemon thread may prevent JVM from exiting. (Will discuss this in detail later in the article). In simple terms, daemon threads run in the background.

Uses of Daemon Thread

Daemon threads are used for background tasks like garbage collection or non-critical tasks like removing unwanted objects from cache, monitoring, and logging, etc.

In general, any task that has a very low priority and can be interrupted in the middle before finishing can be performed using daemon threads.

Examples of Daemon Thread 

class UserThread extends Thread {
    public void run() {
        System.out.println("User Thread starting");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("User Thread finishing");
    }
}
class DaemonThread extends Thread {
    public void run() {
        while(true) {
            System.out.println("Daemon Thread processing");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class DaemonThreadExample {
    public static void main(String args[]) {
        Thread userThread = new UserThread();
        Thread daemonThread = new DaemonThread();
        daemonThread.setDaemon(true);
        userThread.start();
        daemonThread.start();
    }
}

Output

User Thread starting
Daemon Thread processing
Daemon Thread processing
Daemon Thread processing
Daemon Thread processing
Daemon Thread processing
Daemon Thread processing
Daemon Thread processing
Daemon Thread processing
Daemon Thread processing
Daemon Thread processing
User Thread finishing

Now, as we can see through the above example, though the daemon thread has an infinite loop running inside it. It will terminate the moment, all the user threads(in this case only two(user thread and the main thread)) finishes its execution.

When and How not to use Daemon Threads?

Due to daemon thread being low priority thread and JVM being capable of forcing the threads to die when all user threads finish their execution, the finally block inside daemon threads will not execute. Hence, the use of daemon thread should be minimal and also, the design should be done extremely carefully.

For example: IO operation in daemon threads should not be executed because if the thread gets terminated abruptly and the finally block is not executed, resources will not be closed and will lead to a resource leak.

Similarly, in the case of a database connection in the daemon threads, a similar scenario like above can occur which will lead to connection leaks.

Let us also go through an example when even after the execution of all the user threads, Daemon threads prevent JVM from exiting which is the result of a poor design code.

Thread.join()

Before that, let us take a look at Thread.join() as we will be using that in the upcoming example.

Thread.join() – Join method allows a thread to wait for another thread to finish the execution.

Class implements Runnable {
    @Override
    public void run() {
        Thread t = Thread.currentThread();
        System.out.println("Thread started: " + t.getName());
        try {
            Thread.sleep(5000);
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        }
        System.out.println("Thread ended: " + t.getName());
    }
}
public class Main {
    public static void main(String args[]) {
        Thread tc1 = new Thread(new ThreadClass(), "tc1");
        Thread tc2 = new Thread(new ThreadClass(), "tc2");
        // Start first thread immediately
        tc1.start();
        /* Start second thread(tc2) once first thread(tc1)'s
         * execution is completed or the thread is terminated
         */
        try {
            tc1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        tc2.start();
        /* Print the message once, 2nd thread (tc2)'s 
         * execution is completed or the thread is terminated
         */
        try {
            tc2.join();
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        }
        System.out.println("This message will be printed once, both the threads have finished their execution");
    }
}

In the above example because of putting join() on thread tc1, even though thread tc1 went in the waiting state, tc2 did not start before the termination/execution of thread tc1.

Now, let us use this method for a poorly designed code featuring Daemon code.

class UserThread extends Thread {
    public void run() {
        System.out.println("User Thread starting");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("User Thread finishing");
    }
}
class DaemonThread extends Thread {
    public void run() {
        while(true) {
            System.out.println("Daemon Thread processing");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class DaemonThreadExample {
    public static void main(String args[]) throws InterruptedException {
        Thread userThread = new UserThread();
        Thread daemonThread = new DaemonThread();
        daemonThread.setDaemon(true);
        userThread.start();
        daemonThread.start();
        /*
        the following statement will hold the execution of main thread until the daemon thread is running,
         which should not happen because daemon thread's execution should not hold the JVM from exiting
         */
        daemonThread.join();
    }
}

Now, because of the daemonThread.join() statement, the JVM will not be able to exit. Because the main thread cannot finish its execution till the time daemon thread does. Hence, the infinite loop of daemon thread will also result in a never-ending program.

Hence, we should always be very careful about the fact that making a thread daemon using setDaemon(true) might not lead it to shut down immediately after all the user threads have been executed. Hence, we should use them very cautiously and though they seem simple, a good design thought should be put into them.

This brings us to the end of this blog! Hope you enjoyed reading it and I wish you gained something from this.

Thanks for reading!

Share Your Thoughts

Have you every used a Daemon thread in Java? Where and how? Please like and share the article if you found it helpful.

Also Read: How to convert images to pdf in Java

Share:

contributor

I am a Software Developer based in Bangalore, India. I am currently working on building web applications and backend systems associated with it using React, Node.js, Java, and Spring. I am passionate about teaching blogging and thrive to contribute to the tech community through my blog posts.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.