← Back to Multithreading

Thread Scheduling and Daemon Threads

Understand how threads get opportunities to execute and how Java uses daemon threads for background activities.

1. What Is Thread Scheduling?

Imagine you are working on a computer with multiple applications open.

You might be listening to music, downloading a file, and writing a document at the same time.

Your computer manages these activities and gives them opportunities to use the processor.

Something similar happens when multiple threads are running in a Java application.

The process of deciding which thread gets an opportunity to execute is called thread scheduling.

Java uses the underlying operating system and JVM implementation to manage thread execution.

2. Does Java Execute Threads in a Fixed Order?

No. This is one of the most important things to understand about multithreading.

Let's create two threads.

public class SchedulingExample {

    public static void main(String[] args) {

        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 is running");
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 is running");
        });

        thread1.start();
        thread2.start();
    }
}

You might expect the output to always be:

Thread 1 is running
Thread 2 is running

But that order is not guaranteed.

You might also see:

Thread 2 is running
Thread 1 is running

Why?

Because both threads are given an opportunity to execute independently. Their exact execution order depends on thread scheduling.

Remember: Calling start() on one thread before another does not guarantee that the first thread will finish its work first.

3. What Is Thread Priority?

Java allows us to assign a priority to a thread.

A priority is a value that can be used by the thread scheduling mechanism when deciding which thread should get an opportunity to execute.

Java provides priorities from 1 to 10.

  • 1: Minimum priority
  • 5: Normal priority (default)
  • 10: Maximum priority

Java provides constants for these values:

Thread.MIN_PRIORITY
Thread.NORM_PRIORITY
Thread.MAX_PRIORITY

Let's see how to assign a priority.

Thread worker = new Thread(() -> {
    System.out.println("Worker is running");
});

worker.setPriority(Thread.MAX_PRIORITY);

worker.start();

We can also check a thread's priority using getPriority().

int priority = worker.getPriority();

One important thing to remember:

A higher priority does not guarantee that a thread will execute first.

Thread priority is only a scheduling hint. The exact scheduling behaviour depends on the JVM and operating system.

4. What Are Daemon Threads?

Now let's understand another important concept.

Imagine a Java application that performs some important work for a user.

Along with this work, the application may also perform background activities, such as periodically checking something or maintaining supporting information.

These background activities should not necessarily keep the entire application running after its main work has finished.

Java provides a special type of thread for such background activities. It is called a daemon thread.

A daemon thread is a background thread that supports the execution of an application.

Examples of activities that can be handled by daemon threads include background monitoring and periodic maintenance tasks.

5. User Threads vs Daemon Threads

Java threads are commonly discussed in two categories:

  • User Threads: Regular threads that perform the application's main work.
  • Daemon Threads: Background threads that support the application.

The important difference is how they affect the application's lifetime.

The JVM normally continues running while at least one user thread is still alive.

When all user threads have finished, the JVM can exit even if daemon threads are still running.

In simple words:

Daemon threads do not keep the JVM alive.

6. How to Create a Daemon Thread

Java provides the setDaemon() method to mark a thread as a daemon thread.

Let's see a simple example.

public class DaemonExample {

    public static void main(String[] args) {

        Thread backgroundThread = new Thread(() -> {

            while (true) {
                System.out.println("Background thread is running");

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("Background thread interrupted");
                    return;
                }
            }
        });

        backgroundThread.setDaemon(true);

        backgroundThread.start();

        System.out.println("Main thread has finished");
    }
}

Let's understand this example.

  1. We create a thread that continuously performs a background activity.
  2. We call setDaemon(true) to mark it as a daemon thread.
  3. We start the thread.
  4. The main thread prints its message and finishes.
  5. Once no user threads remain, the JVM can exit, even though the daemon thread is still running.

You may see the background thread print its message several times before the application exits.

The exact number of messages is not guaranteed because thread execution is not predictable.

7. An Important Rule About setDaemon()

There is one important rule:

We must call setDaemon() before starting the thread.

For example, this is correct:

Thread worker = new Thread(() -> {
    System.out.println("Working...");
});

worker.setDaemon(true);

worker.start();

But this is incorrect:

Thread worker = new Thread(() -> {
    System.out.println("Working...");
});

worker.start();

worker.setDaemon(true);

Calling setDaemon() after the thread has started throws IllegalThreadStateException.

We can also check whether a thread is a daemon thread using isDaemon().

boolean daemon = worker.isDaemon();

8. What Did We Learn?

  • Thread scheduling decides which threads get opportunities to execute.
  • Java does not guarantee a fixed execution order for multiple threads.
  • Thread priorities range from 1 to 10.
  • A higher priority does not guarantee earlier execution.
  • Daemon threads perform background activities.
  • The JVM can exit when all user threads have finished, even if daemon threads remain.
  • We must call setDaemon(true) before starting a thread.

Remember: Thread scheduling controls execution opportunities, while daemon threads support background activities without keeping the JVM alive.

Test Your Understanding

Let's check what you have learned in this class.

1. What is thread scheduling?

2. Does a higher thread priority guarantee that a thread will execute first?

3. What is a daemon thread?

4. When should we call setDaemon(true)?

5. What happens when all user threads finish but daemon threads are still running?

Class Summary

Let's quickly revise the two concepts we learned today.

  • Thread scheduling determines which threads get opportunities to execute.
  • Java does not guarantee a fixed execution order.
  • Thread priorities range from 1 to 10, but higher priority does not guarantee earlier execution.
  • Daemon threads are used for background activities.
  • The JVM can exit when all user threads have finished, even if daemon threads are still running.
  • Use setDaemon(true) before calling start().

With this, we have completed our second part: Working with Threads!

🎉 Congratulations! Part 2 Completed!

Great job! You have successfully completed Part 2 of our Java Multithreading course.

We started by learning how to pause threads, wait for other threads, and interrupt them. We also explored thread scheduling and daemon threads.

You are gradually building a strong understanding of how threads work in Java. Keep practising the examples and make sure you understand each concept before moving forward.

Take a moment to appreciate your progress. One part completed. Keep going!

What's Coming Next?

Part 3 — Shared Data and Thread Safety

What happens when multiple threads work with the same data? Why can a simple counter produce incorrect results? Let's understand these problems and learn how Java helps us protect shared data.

In this part, we will cover:

  1. Sharing Data Between Threads
  2. Understanding Race Conditions
  3. Synchronization with synchronized Methods
  4. Synchronization with synchronized Blocks
  5. Understanding Locks and Object Monitors
  6. Instance Locks and Class Locks
  7. The volatile Keyword
  8. Atomic Variables and AtomicInteger

We will begin by understanding how multiple threads can access and work with the same data.

See you in Part 3!

0% Completed

You have completed 10 out of 46 classes.