Witscale Test Center

12.2 Conditions that prevent the running of a thread for some time > 12.2.1 Methods in java.lang.Thread that influence the thread scheduling


12.2.1 Methods in java.lang.Thread that influence the thread scheduling

The Thread class has four methods that are influential in thread execution and scheduling. Two of them are static methods, sleep() and yield(). The rest two are instance methods setPriority() and join(). Let us see how to use each of them in detail.

Setting the thread priority

You can increase the chances that your thread is picked from thread pool by increasing its priority with setPriority() method. If you do not set any priority, a normal priority (5) is assigned to a thread. When a thread is created, it gets a default priority that is the priority of the thread of execution that creates it. For example, in the following code the default priority of myThread will be the same priority as thread main. Since main thread does set its property, it will be 5.

 

public class ThreadTester {

 public static void main(String[] args) {

  Runnable myRunnable = new MessageBuddy(“Guest”);

  Thread myThread = new Thread(myRunnable);

  System.out.println(“The default priority: ” + myThread.getPriority());   

 }

}

 

 You can explicitly set the priority to a value between 1 to 10 using setPriority() method. The signature of this method is as:

 

public final void setPriority(int newPriority)

 

The most important thing about thread priority is that it still does not guarantee anything. It means that if you set the thread priority to maximum value (10), it does not mean that it is guaranteed to be picked from thread pool.

 

You should not rely on thread priorities while writing a multithreaded application. The priority does not guarantee anything about the thread scheduling. The priorities are useful to fine-tune your application by increasing or lowering the priority of your threads as per your requirement. But the correctness of your program should never depend on priorities.

 

The Thread class has three predefined constants that specify the range of allowed priorities as well as default (normal) priority. These constants are static final constants and are defined as:

 

public final static int MIN_PRIORITY = 1;

public final static int NORM_PRIORITY = 5;

public final static int MAX_PRIORITY = 10;

 

@

Even if you can set the numerical values, 1 through 10, Java does not guarantee that there will be actually 10 different priorities. For instance, if JVM implementation supports only 5 priority levels, these 10 values might be mapped to 5 values. In such case, even if you set different priories for two threads (say for example 5 and 6), they can have effectively have the same priority. Therefore, the bottom line is you can use priorities but do not expect any guarantees.

 

Sleeping

Sometimes, you just want your thread to do nothing for some predefined time. The static method of Thread class sleep() can help you achieve just that. You can to ask the thread to go to sleep (just do nothing) by calling Thread.sleep(). There are two versions of this method, one of which takes time in milliseconds as argument. Another sleep() method is more accurate as it takes an additional nanoseconds argument to specify more precise time for sleeping. The signatures of these methods are:

 

public static void sleep(long millis) throws InterruptedException

public static void sleep(long millis, int nanos) throws InterruptedException

 

One of major the reasons to ask the thread to sleep is to slow it down if its execution is moving too quickly and that is not what you want. For instance, imagine that you have a thread that downloads an hourly weather update. Now if your thread takes few seconds to download, it has nothing to do for almost an hour. In that case, you should ask it to sleep for some time. Say for instance 59 minutes. Listing 12.2 has two classes. The main method creates and starts the thread weatherUpdaterThread.

 


1

You can also call the sleep method as Thread.sleep() as it is a static method

 

@

Since the sleep method can throw the InterruptedException, you need to wrap the call to it in a try-catch block. Note that the sleeping thread is not interrupted unexpectedly. It has to be explicitly interrupted by another thread by a call. In you code, you would know if the exception is ever going to be thrown and what to do.

 

Once the thread weatherUpdater is created and started, it will eventually execute. In its run method, it first downloads the weather information and then it sleeps for 59 minutes. After 59 minutes (or 59 * 60 * 1000 milliseconds) are elapsed, the thread wakes up. Its state changes from sleeping to ready-to-run. Now again it has to contend with other (ready-to-run) threads in the thread-pool to get its turn for execution. Once it gets it turn, it can resume executing the rest of run method and prints “Sleeping is over”. So if you are asked how much time after the call sleep(59*60*1000) does the message “Sleeping is over” is printed, the correct answer would be ‘sometime after 59 minutes’. You cannot know for sure, as there is no guarantee when your thread will be picked from thread pool for execution. Figure 12.4 shows how the weatherUpdater changes it states once it is started.


Figure 12.5 Execution of thread weather updater thread once it is started and the states it goes through

 

Figure shows how exactly after 59 minutes the thread moves from sleeping to ready-to-run state. Remember that it does start executing right away.

 

Exam often asks this type of questions that tests your knowledge of what is and what is not guaranteed by threads. About a thread that sleeps for predefined time say 100 milliseconds, the best you can say is that it will be executed “Sometime after the 100 milliseconds”. You are only assured that the thread will definitely not execute for 100 milliseconds.

 

Another interesting thing about the example in listing 12.2 is that it will download the whether information only once. The first time it downloads and then sleeps for 59 minutes. After it wakes up it prints the message “Sleeping is over” and the run method is finished. Remember once the run method finishes its execution, the thread is no more a thread-of-execution and it automatically moves in a dead state. Therefore if you want yours weatherUpdater to run forever and keep downloading the weather information every hour, you need to add an infinite loop to run method as:

 

public class WeatherUpdater implements Runnable {

 

   public void run() {

    while(true) { 

    // download weather information

     try {

         sleep(59 * 60 * 1000); //sleeps for 59 minutes

     } catch (InterruptedException exp) {}

     System.out.println(“Sleeping is over”);

    }

   }

}

 

After the thread wakes up and get its turn to print the message “Sleeping is over”, the run method will not finished yet and the next iteration of while loop is executed and the weatherUpdater will download the weather information again.

Yielding

The Thread class has another important static method that can influence the thread scheduling. This method is useful when the thread wants to give up its turn of execution voluntarily. The method signature of yield is:

 

public static void yield()

 

When this method is called the currently executing thread yields (gives up) its turn and moves out of the running state. Listing 12.3 illustrates how to use the yield method.


 

1

You can also call the yield method as Thread.yield() as it is a static method

 

You can call the yield() in a thread’s (say myThread) code as Thread.yield() or myThread.yield(). This will cause the thread which is invoking yield() to yield (give up) its turn-of-execution. Since the myThread’s run method calls yield(), myThread immediately stops its execution and goes back to the ready-to run state. Figure 12.6 shows how yield drives out the currently running thread from the running state.


Figure 12.6 State transitions when the Thread.yield() method is called

 

The important thing to understand about yield is that it stops the execution of the very same thread, which has invoked it. Remember that it is a static method, therefore even if you call it on some other thread, still the same static method gets called. Hence if you change the run method as:

 

public class MyRunnable implements Runnable {

   public void run()  {

      // do some job

      Thread  anotherThread = new Thread();

      anotherThread.start();

      anotherThread.yield();

   }

}

public class Tester {

   public static void main(String[] args)  {

      Thread myThread = new Thread(new MyRunnable() );

       myThread.start();

   }

}

 

Now when the thread myThread is executed, the call anotherThread.yield(); in its run method will cause myThread to yield because it will be the currently executing thread at that time. Bear in mind that yield causes the current thread to yield. The call anotherThread.yield(); does not take any action  for anotherThread. Note that it will not be running at the time of this call.

Yielding and thread priorities

Since Java threading is  very much platform dependent, you cannot be certain if a thread will ever give up its turn-of-execution (turn of using the JVM) to other threads. On some implementations (such as time slicing), the threading algorithm may automatically give different threads a fair share of the JVM time. However, on others (such as priority based), one thread might simply hog the processor. For example, let us assume that thread scheduler is priority based. Now imagine a thread (analyzerThread) that analyses the annual sales data. If the thread pool has threads with lesser or same priority as analyzerThread, the priority-based thread scheduler will allow analyserThread to keep running. This could be especially problematic if analyzerThread is a time-consuming thread (which probably is the case for analyzerThread). Then analyzerThread will continue execution and may take long time to perform analysis. The other threads in thread-pool will starve for JVM and in worst case may never get a chance to execute (especially the threads with lower or same priority). The yield() method comes handy in such situation. You can make periodical calls to yield method in analyzerThread’s code.

 

   public void run()  {

      for(i=0;i<12; i++ ) {    

        // do analysis for one month

        yield();

      }

   }

 

Since this static method causes the currently running thread to yield its turn to execute, above code will yield its turn-of-execution after doing a month’s analysis.  It then leaves the running state and returns to the thread pool with ready-to-run state. Figure 12.7 shows the state transitions since the analyzerThread is started as analyzerThread.start().

 


Figure 12.7 Execution of thread analyzerThread after it is started and the states it goes through

 

Figure shows how, the thread goes through various states. Whenever it gets it turn of execution, it enters running state and partially executes the run method (to do analysis for one month). After that it yields its turn by a call yield() due to which it goes back in the ready-to-run state. Thus it does not greedily grab the JVM when it has its chance. Instead, it is polite enough to give up its turn. Now the thread scheduling system has a better chance to give fair chance to other threads. If no other threads are in the thread pool (in a ready-to-run state), the scheduler may allow the analyzerThread to run again. But again it partially executes the run method (to do analysis for next month) and then it yields its turn by calling yield(). Thus by yielding periodically, the thread does not grab the turn of execution for a long time and creates a window of opportunity for lower or same priority threads to execute.

 

It is often debated whether yield gives all threads (irrespective of priority) an equal chance of execution. It is not true. First, remember that yield is relevant only in case of priority-based scheduling. Secondly, when a slow thread is executing and if a high priority threads enters the thread pool, the high-priority thread will anyway get the turn. The slow thread need not yield for that. The yield() therefore makes difference only in case of same or lower priority threads.

 

Joining

The join() method is an instance method of class Thread. It is used when you want the thread to execute only after some specific thread is executed. Thus, the execution of a thread joins at the end of another thread’s execution. Its method signature is as:

 

public final void join()

 

The join() method is a very useful method when execution of one thread is dependent on another thread’s execution. For instance, if you have a thread that reports about bad weather (reportBadWeather), you probably want to execute it before the thread that updates the weather conditions (weatherUpdater) finishes. 

The thread reportBadWeather must do its job before thread weatherUpdater can complete its work. Therefore, you want to ensure that reportBadWeather is executed completely before weatherUpdater completes. You can do so by “joining” reportBadWeather thread with the thread weatherUpdater. This means that weatherUpdater can not be in state dead until reportBadWeather has completed its execution and moved to a dead state.

 


When Tester is executed, it main thread (that is the main method) starts weatherUpdater. This thread will eventually start running. When it runs the statement reportBadWeather.join(), it stops executing and enters a waiting state. It remains in that state till the thread reportBadWeather dies. This is to make sure that reportBadWeather is executed before weatherUpdater can proceed. Thus, a bad weather is reported before the weather update can be complete. Figure 12.8 shows the state transitions in the execution of Tester listing 12.4.


Figure 12.8 Thread updater stops when badWeather join() method is called. It waits until badWeather thread finishes

 

An overloaded version of join() allows you to ask the thread to wait only certain predefined time. For example, if we say reportBadWeather.join(300), then the thread weatherUpdater stops executing and enters a waiting state. It remains in that state for maximum 300 milliseconds. If reportBadWeather is executed before that, fine. Otherwise, it returns to ready-to-run state after 300 milliseconds. Thus, the call to reportBadWeather.join(300) makes sure that weatherUpdater wait at least for 300 milliseconds and give chance to reportBadWeather for completing its execution. Note that in a call to join(300), bad weather is not guaranteed to be reported before the weatherUpdater can be complete.