This is the third post in this series. If you missed the previous posts, you can find them here:
It’s important to be aware of the various states that a thread can be in when we control threads manually.
In this post, we’ll look at daemon threads and thread groups.
Daemon Threads
The term “demon” is commonly thought of as an evil spirit, but the original term “daemon” meant a spiritual being that influenced a person’s character. Daemon is an older form of the word demon. The ancient Greeks considered a daemon (an alternative spelling is “daimon”) to be a guardian angel or guardian spirit.
In modern usage, the word daemon is pronounced as DEE-mon (the same pronunciation as demon). However, when we are talking about computer software, we tend to pronounce daemon as DAY-mon.
In Java, a daemon thread is a low-priority thread that runs in the background to perform background processing of service-related tasks such as garbage collection.
Daemon threads provide services to user threads. Their only role is to serve user threads. A daemon thread’s life depends on the user threads, i.e. when all the user threads die, the JVM terminates all daemon threads automatically.
Daemon Thread Methods
The public final void setDaemon(boolean state)
method can be called to change a normal user thread into a daemon thread. This method must be called before the thread is started.
Daemon threads are processed in the background, and the main program does not need to wait for them to die before it can exit. JVM terminates itself when all the user threads are finished executing. The JVM can exit when the only remaining running threads are daemon threads. Daemon threads can not prevent the JVM from exiting after all the user threads die. In this context, a daemon thread can be thought as an extremely low priority thread.
We can check if a thread is a daemon thread by calling the public final boolean isDaemon()
method.
Example Code
The following is a simple example of running two threads. One thread is implemented as an anonymous inner class; the other as a lambda expression. Both could have been implemented in either way. The difference is more for purposes of revision and code comparison than for any specific functional requirement.
The first task prints its default name and then enters an infinite loop printing dots. The second task just prints its name and exits. We run the second task twice: once sequentially in the main thread (just for interest) and once in parallel, i.e. as a proper thread.
We set the first task to be a daemon thread with the statement task1.setDaemon(true);
If we do that, the JVM terminates normally after the two user threads (main
and task2
) have completed. The first task is a daemon thread, and as such, the infinite loop doesn’t prevent the JVM from closing down.
However, if we run the first task as a normal user thread by either commenting out the setDaemon(true)
call, or changing it to task1.setDaemon(false);
, then the JVM does not shut down. The user thread running the infinite loop continues to run even after the main
user thread has completed. We have to press Ctrl-C
in the console of a Windows machine to break out of the running program.
In most command line interfaces, Ctrl-C
is understood as an operating system signal instead of a text copy. It is used to stop the currently running task and return control to the command prompt.
public class DaemonThreadTest {
public static void main(String args[]) {
Thread task1 = new Thread (
// anonymous inner class
new Runnable() {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("Hello " + threadName);
while (true) {
System.out.print('.');
}
}
}
);
Runnable task2 = () -> {
String threadName = Thread.currentThread().getName();
System.out.println("Hello " + threadName);
};
// task1 runs in parallel as separate thread
task1.setDaemon(true);
task1.start();
// task2 runs sequentially in main thread
task2.run();
// task2 runs in parallel as separate thread
Thread thread = new Thread(task2);
thread.start();
System.out.println("Done!");
} // end of main
} // end of class
With task1
being a daemon thread, we’ll notice that the application has non-deterministic behaviour. Every time we run it, we’ll get a different output. This can easily be seen by the time of dots being printed, as well as the order in which the thread names and the Done!
message are printed.
Thread Groups
Instead of controlling each thread individually, a thread group can be used to control a number of threads with the thread control methods. We can construct a thread group with the following code:
ThreadGroup g = new ThreadGroup("ThreadGroupName");
Thread t = new Thread(g, "ThreadName");
Almost anything we can do with an individual thread, we can do with thread groups. We can start, stop, resume, suspend, and interrupt the thread group. We can set all threads in the group to be daemon threads, and set the maximum priority of all the threads. We can count, list and enumerate all the threads.
Conclusion
More and more of the iceberg of threads is being exposed. Lots more to come!
In the next post we’ll look at how to interrupt threads and handle them being interrupted. We’ll also investigate the use of the synchronized
keyword, and the wait()
and notify()
methods of the Object
class.
Please share your comments.
Until then, stay safe and keep coding!