THE UNIVERSITY OF WESTERN AUSTRALIA
School of Computer Science & Software Engineering

CITS3211 CONCURRENT SYSTEMS: Part II

Laboratory Exercise 1 (not assessed)

The purpose of this laboratory is to:

Note: you may find it useful to look at the Concurrency Section of the Java Tutorial, which includes similar examples and exercises.

Multithreading in Java

You have already briefly seen in the lectures what is meant by multithreading. We will concentrate mainly on the multithreading and synchronization features of Java in these laboratories. To remind you again the essential features of multithreading:

Java provides two approaches for creating and managing multiple threads. In this lab, we will see the first approach and study it carefully. Then, we will see the limitations of this approach and study a slightly more general and powerful approach.

Creating Threads

In the first approach, you can create multiple threads using the java.lang.Thread class. The following steps are necessary to do this. I am assuming that you understand how inheritance works in Java. We will see examples of these steps in the code which I will supply later.

An Example

Here is an example program illustrating the above concepts. However, we will modify this example program as we progress through this lab. I will explain this program in detail, but only the new features in future programs will be explained in detail. You should save this program in a file called Example1.java.

The output of the program

Before we go into the details of this program, let us execute it. If you execute this program, you will get different output in different executions! We should expect this since there are multiple (two in fact) threads in the program and we do not have any control over the order in which these threads gets scheduled and rescheduled in the machine. This is a sample output :

thread1: Java
thread1: is
thread2: Java
thread1: an
thread2: is
thread1: exciting
thread2: an
thread1: new
thread2: exciting
thread1: language
thread1: for
thread1: concurrent
thread2: new
thread1: programming.
thread2: language
thread2: for
thread2: concurrent
thread2: programming.

The purpose of this program is to print the following message on the console : Java is an exciting new language for concurrent programming. Two threads are created in the program and each thread prints this message word by word. Whenever a thread prints a word, it prints its identity and a ':' just before the word. In our sample output, you can see how the control switches back and forth between the two threads. If you execute the program many times, you will see all sorts of interleaving of the words.

Explanation

Let us now go through this program and try to understand the details.

How does it work?

The program starts as a single thread of execution when the execution of the main(..) method starts. However, two new threads are created immediately after that. So, there are now three threads of control within the program and they all execute independently and simultaneously. You should be able to understand the meaning of the word simultaneously.

Once the two new threads are created, they have all the methods in the class MyThread since they are instances of this class. In addition to that, they inherit all the methods of the Thread class. Also, they share the same address space and hence the array message[]. Now they go on printing the message word by word (and wait in between words) until the array message is completely printed. Also, the control switches back and forth between the two threads (the CPU is scheduled and rescheduled) and we see the different interleaving of the words.

Tasks

The Second Approach for Multithreading

You have already created multiple threads by extending the java.lang.Thread class. The class you created was below the Thread class in the class-hierarchy. However, in the following approach, we need not do this and our class can be placed anywhere in the hierarchy.

Creating Threads

In this approach, your class will implement the java.lang.Runnable interface. The Runnable interface consists of a single method, run(): your class must provide an implementation of it. This new run() method provides an entry point into your thread's execution. Your class is not a specialisation of the Thread, but you can obtain a reference to the thread by passing your Runnable object to a constructor of class Thread.

Here is an example program which creates two threads using the above ideas. This program performs the same tasks as the above. You should be able to understand the details by yourself. However, here are some points to note :

Synchronization

The file UnsynchProducerConsumerTest.java contains an unsynchronized version of the producer consumer example from lectures. Have a look at the code, and try to figure out what will happen when you run it. Then, try actually running it. Were you correct? What exactly is going on?

Fix the program by appropriately making methods synchronized, and using wait and notifyAll.

April, 2008.