Java Thread Scheduler
The thread scheduler in Java is an important concept in multi-threading which is part of the JVM. The scheduler decides which thread to pick up for execution and mainly depends on two factors: time-slicing and preemptive scheduling.
A thread scheduler picks up those threads that are in the Runnable state. When there are multiple threads in Runnable state, there is no guarantee which thread it picks up first.
Criteria for thread scheduling
Below are the different factors based on which a Java thread scheduler works:
Priority: Every thread has a priority which is an integer value between 1 and 10 where 1 is the lowest and 10 is the highest. Thread scheduler picks up threads first which has a high priority. In case multiple threads have the same priority, then it schedules in a random manner.
Arrival time: Another factor is arrival time which means when the thread is ready for scheduling. Suppose different threads have the same priority, then it considers the arrival time of the threads for pick up. The thread that arrives first or has a larger waiting time will be considered first for scheduling by the thread scheduler.
Nature of threads: A thread scheduler also considers the nature or type of thread. In this case, the daemon threads are given less preference and are considered only when there are no more user threads for execution.
Working of a thread scheduler
A thread scheduler in Java works on different mechanisms like preemptive scheduling, time slicing, etc which we will discuss in detail in this section.
A process consists of several threads where every thread has a priority and waiting time. Based on these 2 factors, the thread scheduler picks up the thread for execution and sends it to the processor.
- Initially, the processor has 3 threads with priority 9,3, and 5. Hence based on the priority, the scheduler picks up Thread 1 with priority 9 and sends it to the processor.
- Next, it picks up Thread 3 with priority 5 for execution.
- Suppose another thread Thread 4 with priority 7 comes into the waiting pool. Now, the scheduler pauses the execution of Thread 3 and preempts it. It then picks up Thread 4 for execution since it has a higher priority. This process is preemptive scheduling.
- Once thread 3 and 4 complete execution and another thread Thread 5 with priority 3 which is the same as Thread 2 enters. Based on the FCFS concept, the scheduler picks up Thread 2 for execution since it has a long waiting time and arrived first.
Now, let’s see in detail the techniques used by the Java thread scheduler.
Preemptive scheduling
Generally, the thread scheduler always considers high priority threads for execution. When the processor is already executing a high priority thread, and if another high priority thread comes into the active pool, then the thread scheduler preempts the already executing thread from the processor and moves it to the waiting state, and allows the new high priority thread to execute. This process is known as preemptive scheduling where the currently executing thread is preempted when a higher priority thread enters and the latest thread is given preference.
Time slicing scheduling
Time slicing is the process in which the Java thread scheduler assigns a fixed time slot to all threads for execution. The threads execute for a specific time and then re-enters the active pool again. This process continues until the processor executes all the threads. The specific time slot allocated is called a time slice. The scheduler assigns the CPU for each thread based on the time slice. Once the thread execution is complete, it terminates and proceeds with other thread execution.
First come first serve based scheduling (FCFS)
First come first serve is similar to a queue where the scheduler picks the thread that came first to the ready pool for execution. In other words, the thread scheduler gives high priority to the threads that have more waiting time or that arrives first at the pool. The thread scheduler in Java considers this factor when there are multiple threads of the same priority and decides which thread to execute first based on the FCFS concept.
Variables defining Thread priority
The Thread class has 3 static variables that define the thread priority:
MIN_PRIORITY: Defines the minimum priority for a thread which is 1.
NORM_PRIORITY: Defines the default priority which is 5.
MAX_PRIORITY: Defines the maximum priority which is 10.
Methods dependent on Thread priority
public final int getPriority(): Returns the priority of the thread.
public final void setPriority(int p): Sets the new priority for the thread.
Example: Thread scheduler
Below is an example that demonstrates the working of a thread scheduler. We create 3 classes X, Y, and Z that extends the Thread class. Each class has a run() method that prints the values of the corresponding increment variable. In the main class, we create a thread object and assign priorities for all the 3 classes. Since class X has the highest priority, the scheduler starts class X execution and performs based on time slicing. Next, it takes class Z which has the normal priority and finally it picks up class Y that has the least priority.
class X extends Thread { int a=1; public void run() { System.out.println("Thread X started"); while(a<=3) { System.out.println("Value: " + a + " in Thread X"); a++; } System.out.println("Thread X completed"); } } class Y extends Thread { int b = 1; public void run() { System.out.println("Thread Y started"); while(b<=3) { System.out.println("Value: " + b + " in Thread Y"); b++; } System.out.println("Thread Y completed"); } } class Z extends Thread { int c = 1; public void run() { System.out.println("Thread Z started"); while(c<=3) { System.out.println("Value: " + c + " in Thread Z"); c++; } System.out.println("Thread Z completed"); } } public class ThreadScheduler { public static void main(String[] args) { System.out.println("Main thread"); X x = new X(); Y y = new Y(); Z z = new Z(); Thread t = Thread.currentThread(); System.out.println(t.getName()); t.setPriority(Thread.MAX_PRIORITY); y.setPriority(Thread.MIN_PRIORITY); z.setPriority(Thread.NORM_PRIORITY); x.start(); y.start(); z.start(); } }
Main thread main Thread X started Thread Y started Thread Z started Value: 1 in Thread X Value: 2 in Thread X Value: 1 in Thread Y Value: 3 in Thread X Thread X completed Value: 1 in Thread Z Value: 2 in Thread Z Value: 3 in Thread Z Thread Z completed Value: 2 in Thread Y Value: 3 in Thread Y Thread Y completed