Thread scheduling

When a Java thread is created, the thread inherits its priority from the parent thread. The priority of the thread varies from MIN_PRIORITY to MAX_PRIORITY, where the default priority is NORM_PRIORITY. After a thread is created, the setPriority method can be used to alter the priority of the thread.

Java application developers must be aware of the following consideration while designing or enabling multithreaded Java applications for Nonstop systems:
  • A selfish thread is a thread that executes in a tight loop without giving up control either by explicitly yielding or by initiating a blocking operation.

  • Since the thread-scheduling algorithm on Nonstop is nonpreemptive and does not time slice, it is possible for such a thread to prevent other threads from getting access to CPU time. Theoretically, such a thread can run forever. However, after a while, the operating system periodically reduces the priority of the process in stages, until its priority reaches to a lower value.

For a demonstration of scheduling on a NonStop system, review the results and output of the following programs:

Snnipet for ThreadTimeSlice feature

import java.io.*;
import java.lang.*;

class LoopThread extends Thread {
   private Thread t;
   private int threadNum;
   private static int tick = 1;
   private static volatile int numThreadsStillRunning = 0;
   private static int maxThreads = -1;
   private static volatile int numThreadsCreated = 0;
   public  static void setMaxThreads(int num) { maxThreads = num; }

   LoopThread( int num){
      if ( num <= 0 || num > maxThreads ) {
         System.out.println("Invalid thread number: " + num);
         System.exit(1);
      }
      threadNum = num;
      System.out.println("Creating " + threadNum );
      numThreadsCreated++;
      numThreadsStillRunning++;
   }
   public void run() {
      if ( threadNum == 1 ) {
         // First thread. Execute until other threads terminates
         while ( numThreadsCreated < maxThreads ||
                 numThreadsStillRunning > 1 ) {
            tick++;
         }
      } else {
         // Sleep some time to guarantee first thread runs
         try {
            Thread.sleep(10);
         } catch ( InterruptedException e ) {
            System.out.println("Sleep interrupted.");
         }
         for (int i=0; i < 10000; i++ ) {
             tick++;
         }
      }
      System.out.println("Thread " + threadNum + " exiting.");
      numThreadsStillRunning--;
   }
}

public class ThreadPreemptionDemo {
   public static void main(String args[])
   {
      int numThreadsToCreate = 2;
      // LoopThread[] threads;

      if ( args.length > 0 ) {
         // User has specified number of threads to create
         numThreadsToCreate = Integer.parseInt(args[0]);
         if ( numThreadsToCreate < 2 ) {
            System.out.println("Invalid argument. Minimum value = 2");
            System.exit(1);
         }
      }
      LoopThread[] threads = new LoopThread[numThreadsToCreate];
      LoopThread.setMaxThreads(numThreadsToCreate);
      for (int i = 0; i < numThreadsToCreate; i++) {
         threads[i] = new LoopThread(i+1);
         threads[i].start();
      }
      try {
         for (int i = 0; i < numThreadsToCreate; i++) {
            threads[i].join();
         }
      } catch (InterruptedException ex) {
         System.out.println("Exception " + ex);
      }
   }
}

On the NonStop system, the execution of threads are not time sliced. When a thread gets a chance to run, it enters the loop and starves the other thread. Hence, the application hangs.

Output when thread time slice is not enabled:
java -cp . ThreadPreemptionDemo
Creating 1
Creating 2
WARNING:

You will observe a hang when thread time slice option or time slice policy option is not specified. Therefore, “Creating 2” in the output may or may not be printed.

The Java runtime supports a simple, deterministic, scheduling algorithm known as fixed-priority scheduling. The Java runtime does not time-slice. For the NonStop system, the thread-scheduling algorithm is not preemptive; that is, a thread continues to run until it explicitly yields or otherwise causes a yield by initiating a blocking operation on the thread. When a thread gives up control, the runnable threads of the highest priority are run in first-in-first-out order. A lower priority thread is run (also in first-in-first-out order) only when no runnable threads of a higher priority are available.

Thread time slice

NSJ includes a JVM-forced, preemptive thread scheduling feature. This feature also provides an option to specify the time slice for threads. A thread will run for the specified time slice, after which another thread gets dispatched from the ready queue. This helps in force-yielding a thread which consumes large processor time so that the other ready threads also get the processor time to run.

To enable pre-emptive user threads, use the following option:

-XX:ThreadTimeSlice[=T]

Where,

T specifies the time in milliseconds (ms).
  • T is an optional argument.

  • The default value is calculated by dividing 400 ms by the number of cores on the CPU. For example, if a CPU has four cores, the default time slice value is 400 ms / 4 which is 100 ms.

  • The value of T can range between 1 to 32767.
    • If the specified value of T is above 32767, the value is time-sliced to 32767.

    • If the specified value of T is 0, the default value is used for time slice.

Following is the output for the snippet provided in Example:

Output when thread time slice is enabled
java -cp . -XX:ThreadTimeSlice=10 ThreadPreemptionDemo
Creating 1
Creating 2
Thread 2 exiting
Thread 1 exiting