Real-Time Systems
Exercise 1: Tools and
Threads
2006 Version
The aims of this exercise are:
-
to become familiar with the programming tools that you will use during
exercises and projects
-
to learn how to use threads in Java
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.
-
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)
-
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.*
-
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
-
Study the documentation of the classes AnalogIn and AnalogOut.
Sun's Java tutorial is a good source of information about Java.
-
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 :
-
Object-oriented programming concepts
-
Language basics
-
Classes and Objects
-
Interfaces and Inheritance
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.
-
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
-
Explore the different JDE menu choices. The following menu choices are
specially useful:
-
Java -- Comment out region / Uncomment region
-
Java -- Indent line or region (in the case the automatic indentation has
been mingled)
-
Classes -- class "class-name" (shortcuts to declarations)
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.
-
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;
-
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.
-
Compile the class again, and then run it as a program with the command
>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);
-
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();
-
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.
-
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.
-
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.
-
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.
-
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();
}
-
Define a new class Base in a new file.The class may be empty.
-
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.
-
Since the instances of PeriodicRunnable are no longer instances
of Thread, the starting of the thread is now done as
PeriodicRunnable m = new PeriodicRunnable(...);
Thread t = new Thread(m);
t.start();
-
Another difference is that the static method sleep() belonging
to class Thread no longer may be called directly. Instead it must
be called through the class name, i.e., as
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.
-
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
}
};
-
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.
-
Modify the code of Periodic so that it prints out its priority
before entering the while loop.
-
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.
-
Modify Periodic so that it executes at a priority that is one
above the base priority.
-
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.
-
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.