Real-Time Systems

Exercise 1: Tools and Threads

2006 Version



The aims of this exercise are:

The exercise is developed for Linux (Red Hat, SDK 1.5.1). However, the exercises can also be done on other platforms, e.g., Windows or Solaris.


Key Cards

In order to get access to the computer lab you will need a key card. The key card must be registered for entrance to the M-building and registered for entrance to Automatic Control's labs. Fill in the necessary forms provided by your lab assistant.


Course Home Page

The course home page is  http://www.control.lth.se/~kurstr  Make sure that you are familiar with the contents of this page. The most important parts are the News and the Course Material.


Java Documentation

When programming in Java it is useful to always have a Web-browser window open that shows the JavaDoc documentation for Java.
  1. Open a Netscape Navigator window that shows the Java 2 Platform, API Specification. Use the local mirror accessible via the course home page rather than Sun's original (Local mirror: http://www.control.lth.se/intern/dator/java/jdk1.5.0/docs/api/index.html)
  2. Look up the Thread class and check which methods that are available
It is also useful to have a Navigator Window open that shows the API for the local classes in se.lth.control.*
  1. Open a new Navigator window that shows the API for se.lth.control.* available at http://www.control.lth.se/~kurstr/InternDator/java/regler/index.html
  2. Study the documentation of the classes AnalogIn and AnalogOut.
Sun's Java tutorial is a good source of information about Java.
  1. Go to Sun's tutorial. Those of you without any experience of Java are recommended to go through the following in the trail Learning the Java Language :

Xemacs

It is recommended that you use the JDE Java-mode in XEmacs during software development. JDE gives you automatic Java-style indentation and syntax highlighting.
  1. Save the file  HelloWorldApp.java (if you do the exercise from a computer outside the LTH domain please instead use  HelloWorldApp.java )in your directory. Open xemacs on this file. All Java source files must have the extension .java. Normally, each file contains only one class, which must be declared public. The name of the source file should be the "name-of-the-public-class".java
  2. Explore the different JDE menu choices. The following menu choices are specially useful:
JDE also has a debug mode that gives you the possibility to set and clear breakpoints, single step the execution, generate traces, and display variable values. However, the debug mode may be somewhat difficult to use when several threads are used. If you are interested in this please explore the help system.


Threads

This exercise involves writing the simple Java program Periodic that uses threads. It is essential that you understand the meaning of all words that are indicated by italic letters.
 

Specification

The program will start a number of threads that executes periodically. The threads simply print their period on standard output once each period as below.
>java Periodic 500 2000
500, 2000, 500, 500, 500, 500, 2000, 500, 500, 500, 500, 2000, ...
The program starts one thread per input argument, with the period in milliseconds specified by that argument.
  1. Create a new file Periodic.java. This is the file for your program source code. Declare the class Periodic as
public class Periodic extends Thread {

}

The class is declared to extend the Thread class, so that instances of the class may run as threads. Another way to express this is that Periodic is a subclass of Thread. In this program we want to be able to specify a period for each thread, i.e. each instance of Periodic. Therefore we add an instance variable to hold the period for each Periodic object:
    int period;
  1. Compile the class from the X-terminal issuing the command
>javac Periodic.java
If the compilation returns with errors or warnings you should check the syntax, and make sure that your Java environment is setup correctly.

Declaring a Java program

To be able to run the class as a program we need to define the main() method.
  public static void main(String[] argv) {

  }
The main() method is called once by the JVM when running a class as a program.
  1. Compile the class again, and then run it as a program with the command
  2. >java Periodic
    
    Of course not much happens yet. Actually no thread was created when we ran the program, because no instance of the class was created.

Creating a class instance

To create a class instance, or an object, we need to call a constructor method of the class. When we create the Periodic instance we also want to set the period of that instance. Therefore a constructor method is defined as
public Periodic(int p) {
  period = p;
}
Now let main() call the constructor method
  Periodic m = new Periodic(1000);
  1. Compile and run the program.
A Periodic object (which is also a Thread object) with period 1000 ms was created when we ran the program, and a reference to that object was stored in the variable m. But still nothing visible happened, because we have not specified what code the thread should execute, neither have we started the thread.

Running threads

The code that a running Thread object executes is defined in the run() method. The run() method of the Thread class is defined as empty. To include our own code we override the run() method in our subclass Periodic. We want the object to print it's period on standard output. Therefore we define it as
  public void run() {
    System.out.print(period);
    System.out.println(", ");
  }
To start the thread we call the start() method of the Thread class after the object creation in main(), i.e.,
    m.start();
  1. Compile and run the program.
Now we are getting somewhere! The Periodic thread printed its period on the standard out stream, but only once.  This is because the run() method terminated after printing once, and as a result the thread stopped. To get the thread to continue and print periodically, we need to add an iteration, and to make it wait period milliseconds before printing next. Let us use an eternal while loop:

        while (true) {
        }

To make the thread wait for a certain time we call the sleep()method of the Thread class.

  try {
    sleep(period);
  }  catch (Exception e) {}
Since the sleep() method may throw an exception if the waiting is interrupted by something, we need to use the
try/catch construction. Also change the line System.out.println(", ") to System.out.print(", "), so that the printouts occur on the same line.
  1. Compile and run the program.
The next step is to create multiple threads with different periods to run in parallel. This is easy now. Just create and start some other Periodic objects from the main() method as above.
  1. Compile and run.

Reading program arguments

The last step is to read the input arguments to the program and start the corresponding threads. The program input arguments are delivered to the main() method in the argv argument, as an array of String objects. The length of the array argv can be determined by the expression argv.length.
 
  1. Use the expression  Integer.parseInt(argv[i]) to convert the i'th input argument string argv[i]to an int. Use a for construction to loop through the program input arguments and construct the corresponding Periodic threads. Try to run the program with some different input arguments, e.g.,
>java Periodic 500 1000
    See if you can find situations when the output on the screen is corrupted. The screen is a shared resource. In Exercise 2 you will learn how to ensure that shared resoures are accessed under mutual exclusion.
     
  1. How many threads are involved in the execution of the above command? You may use the method Thread.activeCount() if you are not sure about the correct answer.

Creating threads by implementing runnable

A drawback with creating threads by extending the Thread class is that it is then not possible for the active class to be a subclass of some other class than Thread, e.g., a subclass of some user-defined class. The reason for this is that Java only supports single inheritance, and not multiple inheritance. An alternative way to define an active class is to specify that the class should implement the Runnableinterface, shown below:

     public interface Runnable {
    public void run();
  }
 

  1. Define a new class Base in a new file.The class may be empty.
  2. Create a new class PeriodicRunnable in a new file. It should extend Base and implement Runnable. The class should have the same functionality as Periodic.
    PeriodicRunnable m = new PeriodicRunnable(...);
  Thread t = new Thread(m);
  t.start();
     Thread.sleep(...);
 

Internal threads

A third possibility is to have threads as inner classes, i.e., classes that are defined within another class. The inner thread classes typically extend Thread.
 
  1. Create a new class PeriodicWithInnerThread that extends Base. Inside the class definition you should define the inner class PeriodicThread by extending Thread. Use a thread reference in PeriodicWithInnerThread to reference an instance of the inner PeriodicThread class. The functionality should be the same as before.
An advantage with inner classes is that all variables and methods visible in the outer class can be directly accessed from the inner class. Another advantage is that it is possible for an active object to contain multiple execution threads.

It is also possible to let the inner class be an anonymous class. Using this approach the class  PeriodicWithInnerThread
would contain the following code:

    public class PeriodicWithInnerClass extends Base {

      Thread t = new Thread() {
                               public void run() {
                               // Code to be executed
                               }
                              };

  1. Study the class PeriodicWithAnonymousThread. Make sure that you understand it.


A drawback with anonymous classes is that the code often becomes less readable. However, anonymous classes are often used to implement event listeners in Swing.

Thread Priorities

A created thread gets a default priority.
  1. Modify the code of Periodic so that it prints out its priority before entering the while loop.
  2. Do the same thing for PeriodicRunnable. This must be done in a slightly different way. (Hint: Use the static Thread method currentThread)
In real-time applications the priorities of the threads are very important. Investigate how you change the priorities of a thread.
  1. Modify Periodic so that it executes at a priority that is one above the base priority.
  2. Do the same thing for PeriodicRunnable.

Thread Models

Java has two different thread models. In the green-thread model the virtual machine (VM) is responsible for the scheduling of the Java threads, which only exist as abstractions inside the VM. In the native-thread model the Java threads are mapped upon the thread of the underlying operating system, i.e., the Linux threads. The scheduling of the Java threads is subject to the underlying scheduling of the threads by the operating system.

The green-thread model is the standard model for reference implementations of Java. Since it does not require any thread support in the operating system, it is very portable. However, for efficiency reasons modern VMs such as the Linux HotSpot VM used in the course use the native-thread model as default.

In the course we use standard Java and pretend that it is a real-time language, although in reality it is not. However, we want our programs to behave in a way that functionally is the same as if the programs would have been written in some real-time language executing on some real-time operating system. For example, we do not want a low priority thread to preempt a high priority thread.

Consider the following example:
 

public class StarvationTest {

  private HighPriorityThread tHigh;
  private LowPriorityThread tLow;
  double sum = 0.0;

  public StarvationTest() {
    tHigh = new HighPriorityThread();
    tLow = new LowPriorityThread();
    tHigh.start();
    tLow.start();
  }

  class HighPriorityThread extends Thread {

    public void run() {
      setPriority(8);
      System.out.println("High priority thread started.");
      while (true) {
        sum = sum + Math.random();
      }
    }
  }

  class LowPriorityThread extends Thread {

    public void run() {
      setPriority(7);
      while(true) {
        System.out.println("Hello");
        try {
          sleep(5000);
        } catch (Exception x) {
        }
      }
    }
  }

  public static void main(String[] argv) {
    StarvationTest s = new StarvationTest();
  }

}
This is an example where the programmer has made a design error. The high priority thread never releases the CPU. The result will be that the low priority thread is starved, it never gets the chance to execute. The solution is either to let the high-priority thread release the CPU, e.g., by doing a sleep() in the loop, or to give it lower priority than the low priority thread.
  1. Compile and execute the above example. What is the result?
The problem is that the two Linux threads used in the native-thread model to execute the two Java threads, have the same priority and use round-robin time-slicing. The first Linux thread executes the high priority thread. After a while its time slice expires, and the next Linux thread starts executing. It chooses the next available Java thread, the low-priority thread, and starts executing it.

This behaviour is NOT the behaviour that we want on a uni-processor machine. It makes it difficult to detect errors caused by poorly written code. The user believes that everything is correct, even when this is not the case.