Skip to content
Java multithreading 6 min read

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:

ConstantValueMeaning
Thread.MIN_PRIORITY1Lowest priority — runs last if others are competing
Thread.NORM_PRIORITY5Default for every new thread
Thread.MAX_PRIORITY10Highest 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 caseGood idea?
Giving a UI/animation thread more CPU than a background loaderMaybe — test on your target OS
Ensuring a critical task always completes before anotherNo — 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 workReasonable 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, or CyclicBarrier — 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:+UseThreadPriorities and sufficient OS privileges. Without that, setPriority has 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 ExecutorService with a proper thread pool and task queuing over raw thread priorities.
  • Use ReentrantLock(true) (fair mode) or Semaphore(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
  • 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.
Last updated June 13, 2026
Was this helpful?