Thread Priority
Thread priority is a hint you can give the thread scheduler about how much CPU time a thread deserves relative to others. Higher-priority threads tend to be preferred — but the JVM and OS are free to ignore you, so thread priorities are a suggestion, not a guarantee.
Priority Constants
Every Java thread has an integer priority in the range 1 to 10. The Thread class defines three named constants to cover the common cases:
| Constant | Value | Meaning |
|---|---|---|
Thread.MIN_PRIORITY | 1 | Lowest priority — runs last if others are competing |
Thread.NORM_PRIORITY | 5 | Default for every new thread |
Thread.MAX_PRIORITY | 10 | Highest priority — preferred by the scheduler |
Every thread you create inherits the priority of the thread that created it. Because the main thread starts at NORM_PRIORITY (5), all your child threads also default to 5 unless you change them.
Getting and Setting Priority
Use getPriority() to read a thread’s current priority and setPriority(int) to change it. You must call setPriority before starting the thread for the change to have the most effect (though you can call it after too).
public class PriorityBasics {
public static void main(String[] args) {
Thread t = new Thread(() -> System.out.println(
Thread.currentThread().getName() + " priority: "
+ Thread.currentThread().getPriority()
));
System.out.println("Default priority: " + t.getPriority()); // 5
t.setName("WorkerThread");
t.setPriority(Thread.MAX_PRIORITY);
System.out.println("After setPriority: " + t.getPriority()); // 10
t.start();
}
}
Output:
Default priority: 5
After setPriority: 10
WorkerThread priority: 10
Warning: Passing a value outside 1–10 throws
IllegalArgumentException. Always use the named constants or validate your input.
Comparing Priorities in Practice
Here is a classic demo that gives different priorities to multiple threads and watches which one finishes its work first:
public class PriorityDemo {
static class Counter extends Thread {
long count = 0;
Counter(String name, int priority) {
setName(name);
setPriority(priority);
}
@Override
public void run() {
// Burn CPU for a short burst
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < 200) {
count++;
}
}
}
public static void main(String[] args) throws InterruptedException {
Counter low = new Counter("Low", Thread.MIN_PRIORITY); // 1
Counter norm = new Counter("Norm", Thread.NORM_PRIORITY); // 5
Counter high = new Counter("High", Thread.MAX_PRIORITY); // 10
low.start();
norm.start();
high.start();
low.join();
norm.join();
high.join();
System.out.println("Low count: " + low.count);
System.out.println("Norm count: " + norm.count);
System.out.println("High count: " + high.count);
}
}
Output (typical — your numbers will vary, and may look identical on some JVMs):
Low count: 182043210
Norm count: 198761443
High count: 215334987
On a multi-core machine with only three threads, all three threads may run in parallel anyway, making the difference in counts very small or even zero. This is the core reason you cannot rely on priority.
Note: On Linux (where HotSpot maps Java threads directly to pthreads), thread priority is only respected when threads actively compete for the same CPU core. On Windows, priorities map to Win32 thread priority levels and tend to be more noticeable.
Priority Inheritance
When you create a new thread, it automatically inherits the priority of its parent:
public class PriorityInheritance {
public static void main(String[] args) {
// Main thread is NORM_PRIORITY (5)
Thread child1 = new Thread(() -> {
System.out.println("child1 priority: " + Thread.currentThread().getPriority());
// This grandchild inherits child1's priority
Thread grandchild = new Thread(() ->
System.out.println("grandchild priority: " + Thread.currentThread().getPriority())
);
grandchild.start();
});
child1.setPriority(8); // raise before starting
child1.start();
}
}
Output:
child1 priority: 8
grandchild priority: 8
ThreadGroup and Priority Ceiling
A ThreadGroup can enforce a maximum priority ceiling on all threads it contains. Even if you call setPriority(10) on a thread, the group caps it:
public class GroupPriorityCeiling {
public static void main(String[] args) throws InterruptedException {
ThreadGroup group = new ThreadGroup("LimitedGroup");
group.setMaxPriority(6); // ceiling is now 6
Thread t = new Thread(group, () ->
System.out.println("Effective priority: " + Thread.currentThread().getPriority())
);
t.setPriority(10); // request max — will be capped at 6
t.start();
t.join();
}
}
Output:
Effective priority: 6
Tip:
ThreadGroup.setMaxPriority()is one of the few cases where priority has a reliable, deterministic effect — the JVM enforces the cap regardless of the OS scheduler.
When (and When Not) to Use Priority
Thread priority is a blunt instrument. Here is a quick guide:
| Use case | Good idea? |
|---|---|
| Giving a UI/animation thread more CPU than a background loader | Maybe — test on your target OS |
| Ensuring a critical task always completes before another | No — use join(), CountDownLatch, or Future |
| Starvation prevention (low-priority threads must eventually run) | No — the JVM does not guarantee this; use fair locks instead |
| Logging or cleanup tasks that should yield to real work | Reasonable hint, but do not depend on it |
Warning: Relying on thread priority for correctness (e.g., “thread A must run before thread B”) is a serious bug. Use proper synchronization primitives —
join(),CountDownLatch,Semaphore, orCyclicBarrier— to enforce ordering.
Under the Hood
How JVM Priority Maps to OS Priority
The JVM does not schedule threads itself — it delegates to the operating system scheduler. The mapping between Java priority (1–10) and OS priority varies by platform:
- Linux (POSIX): By default, all Java threads run at the same OS nice level (0). The JVM only uses OS-level priority differentiation if the JVM is started with
-XX:+UseThreadPrioritiesand sufficient OS privileges. Without that,setPriorityhas almost no effect on Linux. - Windows: Java priorities map to Win32 thread priority constants (THREAD_PRIORITY_LOWEST through THREAD_PRIORITY_HIGHEST). The Windows scheduler is preemptive and does observe these, so priority differences are more pronounced on Windows.
- macOS: Similar to Linux via Mach thread priorities; differences are subtle.
This platform dependency is the main reason thread priority is unreliable for portability.
Starvation Risk
If you set threads to MAX_PRIORITY carelessly, lower-priority threads may receive so little CPU time that they make no progress — this is called starvation. The JVM provides no aging mechanism (automatically boosting priority the longer a thread waits), unlike some OS schedulers. To avoid starvation:
- Keep high-priority work short and non-blocking.
- Prefer
ExecutorServicewith a proper thread pool and task queuing over raw thread priorities. - Use
ReentrantLock(true)(fair mode) orSemaphore(n, true)when fairness matters — see ReentrantLock.
Virtual Threads (Java 21)
Virtual threads introduced in Java 21 do not support priority — calling setPriority() on a virtual thread is silently ignored. Virtual threads are always NORM_PRIORITY. This further signals that the Java ecosystem is moving away from raw thread priorities as a concurrency tool.
Quick Reference
Thread t = new Thread(task);
// Read priority
int p = t.getPriority(); // default: 5 (NORM_PRIORITY)
// Set priority (must be 1–10)
t.setPriority(Thread.MAX_PRIORITY); // 10
t.setPriority(Thread.MIN_PRIORITY); // 1
t.setPriority(Thread.NORM_PRIORITY);// 5
// Constants
Thread.MIN_PRIORITY // 1
Thread.NORM_PRIORITY // 5
Thread.MAX_PRIORITY // 10
Related Topics
- Thread Scheduler — Understand how the JVM and OS decide which thread gets CPU time, and what preemptive scheduling means.
- Daemon Threads — Background threads that the JVM automatically terminates when all user threads finish.
- Thread Pools & Executors — The modern alternative to raw threads — manage concurrency without worrying about priorities or starvation.
- Naming Threads — Give threads meaningful names alongside priorities to make debugging easier.
- ReentrantLock & Monitors — Use fair locking to prevent starvation instead of juggling thread priorities.
- Virtual Threads (Project Loom) — Java 21’s lightweight threads that make priority largely irrelevant for high-concurrency workloads.