Thread Priority in Java and Daemon Threads: A Beginner-Friendly Guide

  • Last Updated: September 19, 2026
  • By: javahandson
  • Series
img

Thread Priority in Java and Daemon Threads: A Beginner-Friendly Guide

Learn how thread priority in java works, why it’s only a hint to the OS scheduler, and how daemon threads and setDaemon() decide when the JVM exits.

1. Introduction

Say you run a small kitchen. Some orders are urgent, and some can wait. You tell the cook which ones matter more. But the cook still decides the real order of cooking, based on what pans are free. Thread priority in java works a lot like that hint you give the cook.

In Java, every thread carries a priority number. That number is a request. It tells the scheduler, “please run me a bit sooner.” The scheduler listens, but it does not promise anything. The final call sits with the operating system.

This article covers two small topics that go well together. First is thread priority, and how you set it. Second is daemon threads, which are helper threads that quietly run in the background. Both are simple, yet both trip up beginners because they behave in ways you might not expect.

You only need to know what a thread is and how to start one. If you have written a class that extends Thread or passed a Runnable, you are ready to go.

1.1 What This Guide Covers

Here is the plan for this guide:

  • The three priority constants and what their numbers mean
  • How to read and change a priority with getPriority() and setPriority()
  • Why priority is only a hint the scheduler may ignore
  • What daemon threads are and how setDaemon() works
  • How the JVM decides when to shut down, based on user threads
  • Everyday daemon examples you already rely on

2. What Is Thread Priority?

Thread priority is a hint about how important a thread is. Each thread gets a number from 1 to 10. A higher number means “treat me as more urgent.” A lower number means “I can wait my turn.”

The scheduler uses these numbers to help pick which thread runs next. When many threads want the CPU, a higher priority thread may get picked more often. That is the idea, at least. Reality can differ, and we will get to why soon.

For now, hold on to one line: priority is advice, not a command. You suggest, and the system decides.

2.1 The Three Priority Constants

The Thread class gives you three named constants. They save you from remembering raw numbers.

  • MIN_PRIORITY (Thread.MIN_PRIORITY) — the value is 1, the lowest you can set
  • NORM_PRIORITY (Thread.NORM_PRIORITY) — the value is 5, the default for every new thread
  • MAX_PRIORITY (Thread.MAX_PRIORITY) — the value is 10, the highest you can set

Every thread you create starts at NORM_PRIORITY, which is 5. You do not have to set it. Java hands you a sensible middle value out of the box.

You can also pass any number from 1 to 10 directly. But the named constants read better. Someone glancing at your code sees MAX_PRIORITY and knows the intent right away.

2.2 A Quick Look at the Numbers

Let us print the three constants so the values are clear.

System.out.println(Thread.MIN_PRIORITY);   // 1
System.out.println(Thread.NORM_PRIORITY);  // 5
System.out.println(Thread.MAX_PRIORITY);   // 10

Nothing fancy here. Just three integers with friendly names. Now let us see how to use them on a real thread.

3. Setting and Reading Priority

Two methods handle the whole job. One reads the current priority. The other changes it. Both live on the Thread class, so any thread has them.

3.1 The getPriority() Method

The getPriority() method returns the thread’s current priority as an int. Call it on any thread and you get back a number from 1 to 10.

Thread t = new Thread(() -> System.out.println("working"));
System.out.println(t.getPriority());   // 5, the default

See how a brand new thread already sits at 5? That is NORM_PRIORITY doing its job. You never touched it, yet it has a sane value.

3.2 The setPriority() Method

The setPriority() method changes a thread’s priority. You pass a number from 1 to 10, or one of the named constants. Set it before you start the thread, so the scheduler knows from the get-go.

Thread t = new Thread(() -> System.out.println("working"));
t.setPriority(Thread.MAX_PRIORITY);   // now 10
System.out.println(t.getPriority());  // 10
t.start();

One warning: pass a number outside 1 to 10 and Java throws an IllegalArgumentException. So setPriority(11) or setPriority(0) will blow up at runtime. Stick to the range, and you stay safe.

3.3 Priority and the Thread Group

Here is a small twist people miss. A thread also belongs to a thread group, and that group has a max priority of its own. If you set a priority above the group’s cap, Java quietly lowers it to the cap.

You will rarely hit this in day-to-day code. Most threads share the main group with a max of 10. Still, it is good to know that the number you asked for is not always the number you get.

3.4 Priority Is Inherited

A new thread does not pick 5 out of thin air. It copies the priority of the thread that created it. Since the main thread runs at 5, the threads you spawn from main also start at 5.

This matters if you raise a thread’s priority and then let it create more threads. Those child threads will start high too, because they inherit the raised value. Set each one on purpose if you care about the exact number.

Thread parent = new Thread(() -> {
    Thread child = new Thread(() -> {});
    // child inherits the parent's priority
    System.out.println(child.getPriority());
});
parent.setPriority(Thread.MAX_PRIORITY);
parent.start();   // child prints 10, not 5
💡 Interview Insight: A common question is “what is the default priority of a thread in Java?” The answer is 5, which is Thread.NORM_PRIORITY. A follow-up often asks what a thread inherits its priority from. A new thread takes the priority of the thread that created it, not a fixed default. Since main runs at 5, threads you spawn from main also start at 5.

4. Why Priority Is Only a Hint

This is the part that surprises everyone. You set MAX_PRIORITY on a thread, run it, and it still does not clearly beat the others. Why? Because the priority is a request, and the request can be turned down.

4.1 The Scheduler Makes the Call

Java does not run threads by itself. It hands them to the operating system, and the OS scheduler decides who runs and when. Your priority number is passed along as a suggestion. The scheduler may honour it, or it may not.

Think of it like a fast-pass at a theme park. The pass says you should board sooner. But if the ride is down or a bigger group came first, you still wait. The pass is a hint, not a guarantee.

4.2 It Changes Across Systems

The same code can behave differently on different machines. Windows, Linux, and macOS each run their own scheduler. Some map Java’s ten levels neatly. Others squash them into just a few real levels. A few mostly ignore priority for normal threads.

So a thread that seems to win on your laptop might tie on a server. That is not a bug. It is the scheduler being the scheduler.

4.3 What This Means for You

Do not build your program’s correctness on priority. Never assume a high priority thread finishes first. If order truly matters, use proper tools like join(), locks, or a queue. Those give real guarantees.

Treat priority as a gentle nudge for performance, nothing more. It might help a background task stay out of the way. It will not enforce any order you can count on.

4.4 Trying It Yourself

You can run a small test to feel this in action. Start two threads, give one MAX_PRIORITY and the other MIN_PRIORITY, and make each count in a loop. Then see which finishes first.

Runnable job = () -> {
    for (int i = 0; i < 1_000_000; i++) { /* busy work */ }
    System.out.println(Thread.currentThread().getName() + " done");
};
 
Thread high = new Thread(job, "HIGH");
Thread low  = new Thread(job, "LOW");
high.setPriority(Thread.MAX_PRIORITY);
low.setPriority(Thread.MIN_PRIORITY);
 
low.start();
high.start();

Run this a few times on your machine. Sometimes HIGH wins, sometimes LOW wins, and sometimes they tie. The result is not fixed. That wobble is the whole lesson: the scheduler, not your number, has the final say.

💡 Interview Insight: Interviewers love to ask “does higher thread priority guarantee earlier execution?” The clean answer is no. Priority is only a hint to the OS scheduler, and behaviour varies by platform. If someone needs ordering, point them to synchronization tools, not priority numbers. Saying this out loud shows you understand how Java threads really run.

5. What Are Daemon Threads?

Now to the second topic. A daemon thread is a background helper. It runs quietly and supports the main work of your program. When the real work is done, a daemon thread does not hold things up. It just stops with the rest.

The word daemon here means a background worker. It is not scary, despite the spelling. Think of it as a caretaker that tidies up while the main show goes on.

5.1 User Threads vs Daemon Threads

Java splits threads into two kinds. The difference is simple but important.

  • User thread — the normal kind. It does the main work, and the JVM waits for it to finish.
  • Daemon thread — a background helper. The JVM does not wait for it, and can exit while it still runs.

Every thread you make is a user thread by default. You have to flip a switch to turn one into a daemon. We will see that switch next.

Here is a side-by-side view of the two, so the split stays clear in your head:

PointUser ThreadDaemon Thread
PurposeMain work of the appBackground support work
JVM waits?Yes, until it endsNo, exits without it
Default?Yes, every new threadNo, must set the flag
Set withNothing neededsetDaemon(true)
Good forPayments, file writesCleanup, logging, cache

5.2 The setDaemon() Method

You mark a thread as a daemon with setDaemon(true). There is one rule: call it before start(). Try to set the daemon flag after the thread starts, and Java throws an IllegalThreadStateException.

Thread helper = new Thread(() -> {
    while (true) {
        // do some background chore
    }
});
helper.setDaemon(true);   // must come before start()
helper.start();

You can also check the flag with isDaemon(). It returns true for a daemon thread and false for a user thread. Handy when you are not sure what you are dealing with.

5.3 Daemon Status Is Inherited

A new thread copies the daemon status of the thread that made it. So a thread born from a user thread is also a user thread. A thread born from a daemon is a daemon too, unless you change it before starting.

The main thread is a user thread. That is why every thread you spawn from main is a user thread by default. It simply inherits what main already is.

6. How the JVM Decides to Exit

Here is the rule that ties daemon threads together. The JVM keeps running as long as at least one user thread is alive. Once the last user thread ends, the JVM shuts down. It does not wait for any daemon threads still running.

6.1 The Simple Rule

Let us say it plainly:

  • At least one user thread alive — the JVM stays up and keeps working.
  • No user threads left, only daemons — the JVM exits right away.
  • Daemon threads still running at exit — they are stopped abruptly, mid-task.

That last point matters. A daemon can be cut off in the middle of its work. So do not put important cleanup, like closing a file or flushing data, only inside a daemon. It might never finish.

6.2 Seeing It in Action

This example shows a daemon that would loop forever. Yet the program still ends, because main is the only user thread and it finishes fast.

public class DaemonDemo {
    public static void main(String[] args) {
        Thread bg = new Thread(() -> {
            while (true) {
                System.out.println("daemon working...");
            }
        });
        bg.setDaemon(true);
        bg.start();
 
        System.out.println("main is done");
        // main ends here, so the JVM exits and the daemon stops
    }
}

Run it and you get a few daemon lines, then the program quits. The infinite loop never traps you. Once main ends, no user thread is left, so the JVM walks out and takes the daemon with it.

6.3 A Quick Note on the Thread Lifecycle

You might wonder how a thread moves from new, to running, to dead. That full journey is its own topic. We covered it in the Thread Life Cycle article, so head there if you want the state-by-state walk.

💡 Interview Insight: A favourite question is “will the JVM wait for daemon threads to complete?” The answer is no. The JVM exits once the last user thread ends, and any daemon threads are stopped without ceremony. This is why you should never rely on a daemon for critical shutdown work.

7. Common Daemon Threads You Already Use

Daemon threads are not some rare thing. They run inside every Java program you write, doing quiet background jobs. You just never had to think about them.

7.1 The Garbage Collector

The most famous daemon is the garbage collector. It runs in the background and frees memory you no longer use. You never start it, and you never wait for it. It works while your code works, then stops when the JVM stops.

This is the perfect daemon job. It supports your program without being part of the main task. If the JVM is ready to exit, the collector has no reason to hold it back.

7.2 Background Savers and Timers

Plenty of libraries spin up daemon threads for background chores. A few common ones include:

  • Auto-save workers that flush data every few seconds
  • Timer threads that fire scheduled tasks in the background
  • Log-writing threads that push messages to a file off the main path
  • Connection-pool keepers that watch idle database connections

The pattern is the same each time. These threads help, but they are not the point of the program. So they run as daemons and never block the exit.

7.3 When to Make Your Own Daemon

Make a thread a daemon when it does support work that should not keep the app alive. A cache cleaner, a heartbeat pinger, or a metrics reporter all fit well. If the app is done, these can safely stop.

Keep a thread as a user thread when its work must finish. Writing an important file, completing a payment, or saving user input should be a user thread. You want the JVM to wait for those.

8. When Does Priority Actually Help?

By now you may think priority is useless. It is not. It just has a narrow, honest job. Used the right way, it can gently shape how your program shares the CPU.

8.1 Keeping Background Work Out of the Way

A common good use is to lower priority, not raise it. Say you have a background task that indexes files or crunches numbers. Give it MIN_PRIORITY. Now it tends to yield when your main work needs the CPU.

This keeps the app feeling snappy. The heavy task still runs, but it steps back when something more urgent shows up. Lowering priority is often safer than raising it.

8.2 Nudging, Not Forcing

Think of priority as a soft preference for performance. It can tilt the odds a little. It cannot lock in an order or a deadline. If you only need “this task should usually wait its turn,” priority fits fine.

The moment you need a hard rule, drop priority and pick a real tool. A CountDownLatch, a join(), or a blocking queue will do what priority never can.

8.3 Daemon Threads and Shutdown Hooks

Since a daemon can die mid-task, how do you clean up on exit? The answer is a shutdown hook. It is a special thread the JVM runs while it is closing down, so you get a last chance to tidy up.

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    System.out.println("cleaning up before exit");
    // close files, flush logs, release resources
}));

Pair a daemon worker with a shutdown hook and you get the best of both. The daemon does light background work and never blocks the exit. The hook handles the important cleanup when the app actually shuts down.

9. Common Mistakes and Pitfalls

Both topics look easy, and that is the trap. Here are the slips that catch beginners most often.

9.1 Trusting Priority for Order

The biggest mistake is treating priority like a promise. People set MAX_PRIORITY and assume that thread runs first, every time. It does not. If you need order, reach for join() or a lock, not a priority number.

9.2 Calling setDaemon() After start()

The daemon flag has to be set before the thread starts. Call setDaemon(true) after start() and you get an IllegalThreadStateException. Always flip the flag first, then start the thread.

9.3 Putting Cleanup Inside a Daemon

Never rely on a daemon thread for important cleanup. Since the JVM can kill it mid-task, your file may stay half-written or your data unsaved. Keep critical finishing work in a user thread, or use a shutdown hook.

9.4 Setting a Priority Out of Range

Any value below 1 or above 10 throws an IllegalArgumentException. It is an easy mistake when you pass raw numbers. Using MIN_PRIORITY, NORM_PRIORITY, and MAX_PRIORITY keeps you inside the safe range without thinking.

10. FAQ’s on thread priority in java

Q: What is the default priority of a thread in Java?

A: The default is 5, which is Thread.NORM_PRIORITY. A new thread actually inherits the priority of the thread that created it, so a thread spawned from main starts at 5 because main runs at 5.

Q: Does higher thread priority guarantee earlier execution?

A: No. Priority is only a hint to the OS scheduler, which makes the final call. Behaviour also varies across Windows, Linux, and macOS. If you need a guaranteed order, use synchronization tools like join(), locks, or a queue instead.

Q: What is the range of thread priority in Java?

A: The range is 1 to 10. Thread.MIN_PRIORITY is 1, Thread.NORM_PRIORITY is 5, and Thread.MAX_PRIORITY is 10. Passing any value outside 1 to 10 throws an IllegalArgumentException.

Q: What is a daemon thread in Java?

A: A daemon thread is a low-level background helper that supports the main work of a program, such as the garbage collector. The JVM does not wait for daemon threads, so they stop automatically once the last user thread ends.

Q: Will the JVM wait for daemon threads to complete?

A: No. The JVM keeps running only while at least one user thread is alive. When the last user thread ends, the JVM exits immediately and any daemon threads still running are stopped mid-task. That is why you should never put critical cleanup only inside a daemon.

Q: How do you make a thread a daemon thread?

A: Call setDaemon(true) on the thread before you call start(). Setting the flag after the thread has started throws an IllegalThreadStateException. You can check the flag with isDaemon().

11. Quick Recap

Let us pull the two topics together in a few plain lines.

Thread priority in java is a hint from 1 to 10. You set it with setPriority() and read it with getPriority(). The scheduler may honour it or ignore it, so never depend on it for correctness.

Daemon threads are background helpers you mark with setDaemon(true). The JVM does not wait for them. Once the last user thread ends, the JVM exits and stops every daemon still running.

Use priority as a soft nudge. Use daemons for support work that can stop any time. For anything that must finish or must run in order, use real synchronization tools instead.

Further Reading

 

Leave a Comment