JC256 Session 12: Multi-threaded Applications

Summary of the last session

Last week we:

Introduction to threads

A thread is a sequential flow of control within a program. The idea of a sequential flow is one familiar to us - a program will execute one instruction after another from beginning to end. However, threads differ in that a single program can have multiple threads, running at the same time and performing different tasks.

Using threads in Java is relatively simple, however we must watch out for a few things. Firstly, although multiple threads run at the same time in theory, in reality we must consider how this maps onto the available hardware (and how Java deals with this). Secondly, we must overcome the problems involved when concurrent threads access a shared object "at the same time". Although threads are useful and often necessary, because of these problems "don't use them unless you have to"!

The nuts and bolts needed by the JVM to create and run a thread are provided by the Thread class. Within a thread there must be a special run method that contains the code to implement what the thread does; there are two different ways to provide a run method for a thread:

We will study these two techniques in turn.

Subclassing Thread

The simplest way to customise what a thread does is to subclass the Java Thread class that contains a default, empty, run method. This can then be overridden to provide the functionality needed:

public class ThreadedClass extends Thread {

	public void run() {
        // Do the code we wish to be threaded here!
    }
}

For example, look at SimpleThread:

http://www.ecs.soton.ac.uk/~dem/teaching/proginjava/code/SimpleThread.java

The SimpleThread class inherits from Thread (remember that you can find out more about the Thread class in the Java API). It's constructor takes a String argument and calls the superclass (Thread) constructor, which sets the name of the Thread.

The run() method defines what the class does. On this case it loops ten times; in each loop it prints it's name, prints the iteration of the loop, then waits for a random amount of time (up to one second).

sleep() makes the thread stop for a specified amount of time; also note the use of the Math.random() static method call - you may like to investigate this in the API now as we will use it again later! Once finished, the program tells you.

To start a thread going, we do not call the run() method directly; instead we call the threads start() method (inherited from Thread). This allocates the necessary resources to start a thread and in turn calls the run() method within the JVM as a thread:

public class ThreadTest {
    public static void main (String[] args) {
        new ThreadedClass().start();
    }
}

Of course, because they are threaded we can start two instances of the class "at once":

http://www.ecs.soton.ac.uk/~dem/teaching/proginjava/code/TwoThreadTest.java

Compile and run the examples. The test class main method starts each thread as soon as it is created by calling the start method. What output do you see? Add a third thread to the test class - how does this alter the output? Do you get the same output if you run the test several times? Do you think this is related to the random sleep() call?

Study another example:

http://www.ecs.soton.ac.uk/~dem/teaching/proginjava/code/CountingThreadExample.java

Can you work out which thread prints which numbers? If not, add more information to the println calls to explicitly show which thread is printing. What happens if you run the example again - are the numbers printed in the same order? Even though there is not random value for sleep()? Does this change when you take out the sleep call? Does this mean you can rely on the order threads are processed in? (We will learn more about thread priority later).

The Runnable interface

The Runnable interface is the second way of writing threads. The run method contains the code to be threaded and is defined in the Runnable interface; it must be included in the class to implement Runnable (indeed, the Thread class is a Runnable object). You then create an instance of the Thread class directly, passing a separate instance of the class which implements the Runnable interface as the constructor argument.

The instance of Thread can be used as before; when the Thread instance is start()ed the run() method of the class which implements the Runnable interface is executed as a thread. For example:

public class ThreadableClass implements Runnable
{
	public void run()
	{
		// Code to be threaded goes here!
	}
}

public class TestThread
{
	public static void main (String[] args)
	{
		Thread test = new Thread( new ThreadableClass() );
		test.start();
	}
}

We will look at some applet code which displays a clock in the applet browser. Make sure you create some suitable HTML to launch the applet and use the AppletViewer.

First, look at an initial, unthreaded, version of how we might (wrongly) write this code:

http://www.ecs.soton.ac.uk/~dem/teaching/proginjava/code/BrokenClock.java

Compile and execute it. Why do you think it doesn't work?

Swing updates and event handling are dealt with in the JVM by a special event handling thread - but what if this thread is not given any processing time?

(As a point of interest, Swing is not thread-safe. So many of our Swing examples in todays session are not perfect from a threading point of view. Making Swing code thread-safe is a little more complicated - see the 'Task 2' section for more information).

Now study a version of the applet which does work. Be sure to note that the start() method overridden is the JApplet start() method - within this the Thread start() method is then called:

http://www.ecs.soton.ac.uk/~dem/teaching/proginjava/code/Clock.java

Note that the Clock class is a subclass of Applet and implements Runnable. In the applets start method Clock creates a thread with itself as the Thread's constructor target - remember that the first argument of the Thread constructor must implement the Runnable interface (which it does!). Since the applet is now a thread, you should be able to move the window, resize, scroll etc. while the clock is still counting the time.

We will now look in more detail at how the thread is working. When the clockThread is created as a new thread it is put into a New Thread state. In this state, the thread can only be started, no other method can be called.

The start method creates the system resources necessary to run the thread, schedules the thread to run, and calls the threads run method. At this point the thread is in the running state - although it may still be waiting for CPU time from another thread.

A thread becomes "Not Runnable" when:

Once Not Runnable, the thread may become Runnable again if:

A thread is not stopped by a call to a method like applets. Rather, the run method should be written to naturally terminate, e.g. by completing a loop. In the case of Clock, the run method exits once the current executing thread is not itself.

In this example, that would happen when the applets stop method is called and clockThread is set to null.

In what situations should you implement a thread through the Runnable interface rather than inheriting from Thread? (Think back to our interface work with Swing GUI applications).

Thread Scheduling

Since most computers only have one CPU, threads must share the CPU with other threads. This execution of multiple threads on a single CPU, in some order, is called scheduling. Java implements the simple fixed priority scheduling algorithm, where each thread is given a priority number in relation to the other threads, and the thread with the highest priority is given execution first.

The Java runtime system's thread scheduling algorithm is also pre-emptive. If at any time a thread with a higher priority than all other runnable threads becomes runnable, the runtime system chooses the new higher priority thread for execution. However, threads with the same priority may not be swapped out - Java does not support time slicing (although the underlying architecture may).

To use this simple scheduling mechanism, we set the priority of a thread using the Thread class setPriority() method, passing an integer value as the argument. The higher the value, the higher the priority - simple!

Study, compile, and run, the applet below. It has three threads; two which "run" to output the highest value (and subclass Thread), and one which graphically outputs the values of the runners on the screen (and implements Runnable). Try and spot the code which sets the thread priority - initially it is set to give an unfair advantage to one instance of runner over the other:

http://www.ecs.soton.ac.uk/~dem/teaching/proginjava/code/RaceReporter.java

http://www.ecs.soton.ac.uk/~dem/teaching/proginjava/code/Runner.java

Change the priority of the two "runners" to be equal. What happens now?

Synchronised threads

So far, our threads have been able to work independently since all the data and methods needed by each thread were self contained. We will now consider what happens then concurrently running threads share data and must consider the state and activities of other threads.

The following files represent an example where a Producer thread writes out data to the CubbyHole object, while a Consumer thread reads that same data in from the CubbyHole.

The data the Producer creates is an integer between 0 and 9, and it sleeps for a random amount of time (up to 100 milliseconds) between generating the numbers. As soon as it puts the integer in the CubbyHole object the Consumer takes that integer from the same CubbyHole object.

We would expect the Producer and Consumer thread classes to look like this:

http://www.ecs.soton.ac.uk/~dem/teaching/proginjava/code/Producer.java

http://www.ecs.soton.ac.uk/~dem/teaching/proginjava/code/Consumer.java

We might naively the use code like the following to implement CubbyHole (note: this is not how to do it!):

public class CubbyHole {
    private int contents;

    public int get() {
        return contents;
    }

    public void put(int value) {
        contents = value;
    }
}

What happens if both the Producer and the Consumer access the CubbyHole at exactly the same time? What happens if the Producer thread is quicker and deposits two numbers in the CubbyHole before Consumer has the chance to collect the first? Without synchronisation, the Consumer would skip a number! Or, if the Consumer were faster, it could collect the same number twice!

This situation can lead to problems called race conditions and deadlock, where two threads try to access a single object at the same time. We will study this example to see how the problem can be overcome in Java.

Locking an Object

The first way we synchronise operations is to stop the two threads simultaneously accessing CubbyHole. We do this in Java by locking an object - when an object is locked by one thread and another thread tries to call a synchronised method on the same object, the second thread will block until the object unlocks.

Code segments in a program that access the same object from separate, concurrent threads are called critical sections, and are marked in Java with the synchronized keyword. In our example, the put and get methods of CubbyHole are the critical sections and are marked with the synchronized keyword.

CubbyHole might now look something like this (but it's still not quite right!):

public class CubbyHole {
    private int contents;

    public synchronized int get() {
        return contents;
    }

    public synchronized void put(int value) {
        contents = value;
    }
}

Coordination

As well as object locking we must have some simple coordination between our threads. The Producer must have a way to indicate to the Consumer that the value is ready and the Consumer must have some way to indicate that the value has been collected.

The CubbyHole stores its value in a private member variable called contents.We add a second private member variable to CubbyHole, available, that is a boolean. available is true when the value has just been put but not yet gotten and is false when the value has been gotten but not yet put.

The put() and get() methods can be amended to set the appropriate value of available when they are called.

But this still doesn't fix everything. We could get into a deadlock situation where Consumer is waiting for Producer to put() and Producer won't put() until the Consumer will get() (which in turn won't get() until the Producer has put() - and so on, ad infinitum).

We need a system to notify other threads when another is ready - and Java provides such a mechanism, as demonstrated in our final version of CubbyHole:

http://www.ecs.soton.ac.uk/~dem/teaching/proginjava/code/CubbyHole.java

The code in the get method loops until the Producer has produced a new value. Each time through the loop, get calls the special wait method. The wait method relinquishes the lock held by the Consumer on the CubbyHole (thereby allowing the Producer to get the lock and update the CubbyHole) and then waits for notification from the Producer.

When the Producer puts something in the CubbyHole, it notifies the Consumer by calling notifyAll(). The Consumer then comes out of the wait state, available is now true, the loop exits, and the get method returns the value in the CubbyHole.

The put method works in a similar fashion, waiting for the Consumer thread to consume the current value before allowing the Producer to produce a new one.

The notifyAll method wakes up all threads waiting on the object in question (in this case, the CubbyHole). The awakened threads compete for the lock. One thread gets it, and the others go back to waiting. This method is provided by the top level Object superclass. The Object class also defines the notify method, which arbitrarily wakes up one of the threads waiting on this object.

Finally, here is some code to test the Producer, Consumer and CubbyHole:

http://www.ecs.soton.ac.uk/~dem/teaching/proginjava/code/ProducerConsumerTest.java

Task 1

For this exercise we will model a 'deathmatch' battle between two (particularly stupid) characters in some kind of adventure game. Two instances of the Character class will have to pick up a single instance of a Weapon class, and try to kill each other with the Weapon.

The Character class should inherit from Thread, and thus be threaded. It should maintain a boolean value of whether it is alive, along with a method to report this value. When constructed the Character instance should have a health value of 100, and this is depleted by the value passed to the damage() method. If the health value reaches 0, the Character instance is 'dead'.

When constructed, the Character should be passed 4 arguments: the Weapon instance it is to try to use; the other Character instance is is to fight; a value which represents it's 'speed' or responsiveness; and a value which represents it's 'strike rate'.

Upon running a Character thread, the thread should attempt to access the specified Weapon, and call the Weapons attack() method on the other Character if a random number falls within it's 'strike rate'. Between each attempted strike on it's opponent, the Character should pause for a random time skewed by it's responsiveness value.

If the Character is killed, it must finish it's thread and report of it's death. If it's opponent has died, then the Character must have won.

The Weapon class should be constructed with an argument to represent the health value it can remove from a Character. It's attack() method should be synchronized so that only one character can use the Weapon at a time. If successfully called, the attack() method should remove the specified health value from the Character being attacked using that Characters damage() method.

Write a test class - PlayGame - which creates two instances of the Character thread and one Weapon and starts them fighting. The fight status should be reported if an attack is made, e.g.

Character1 (Health=45) has struck Character2 (Health=80) with the Mace weapon.

Is it possible for the Characters to both kill each other? (Consider what might happen if a Character blocking on the Weapon is killed while being blocked).

To extend the exercise you could:

Task 2

This task is a little more complicated and is for those of you looking for something a little harder. Study carefully the following "Threads and Swing" chapter of the Java tutorial:

http://java.sun.com/docs/books/tutorial/uiswing/overview/threads.html

Note that Swing is not "thread safe" and make sure you know how threaded code must behave if it updates Swing components.

Now write the PlayGame from the previous task as an applet. The commentary for the battle should be reported in a scrolling text pane, and in addition the health of the two Characters should be represented by an updating JProgressBar. Make sure this is updated in a thread safe manner.

In addition there should be editable text fields to enter the attributes for the two Characters and the Weapon, and a start JButton to start the fight.

End of course information

The course notes should remain available at the same address for a while - however if you would like to take a copy home then they can be downloaded in their entirety here (although you may find that the cross site linking doesnt work) :

http://www.ecs.soton.ac.uk/~dem/teaching/proginjava/proginjava.zip

Coursework

If you wish to hand in any courseworks late, you must fill in a deadline extension form tonight. Any courseworks completed after the end of the course should ideally be handed in at the New College Student Information Office, but if this is difficult they can be sent to me directly at:

David Millard
Intelligence, Agents, Multimedia Research Group
Department of Electronics and Computer Science
New Zepler Building (B59)
University of Southampton
Highfield
Southampton
SO17 1BJ