CSE 428: Lecture 13


Concurrent Programming in Java

We will dedicate a group of lectures to the principles of concurrent programming. We will use Java as the reference langauge for our examples and for the practice exercises.

Concurrent Programming

A concurrent program is characterized by the presence of multiple threads of control. This is in contrast to a sequential program, where there is only a single thread of control.

Clearly, the possibility of multiple threads enhances the capabilities of a program. In particular, with concurrent progarmming we can:

There are several reasons why a concurrent program may be preferable to a sequential one. For instance:

The programmer's perspective

Concurrency introduces new concepts and introduces new challenges for the programmer. Examples of new concepts are: Examples of new challenges are

The Java Programming Language

Java has a syntax similar to C and C++, and, like C++, it has Object-Oriented features. One of the main differences with C++ is that it does not require explicit memory management, because it has automatic Garbage Collection. Another difference is that in Java all objectes are allocated dynamically in the heap. To be more precise, assume that we have a class
   class C {
      ...
      void f(){...}
      ...
   }
In C++, we have two ways of creating an object of class C: As a global or a local variable, with a declaration of the form
   C x;
or as a dynamic variable in the heap, by using the "new" operator:
   C* y = new C();
The application of a method to the above objects, in C++, is done in the following ways:
   x.f();
   y->f();  // syntactic sugar for (*y).f()
In Java, only the second way of object creation is available. Namely, the only way of creating an object is the following:
   C y = new C();
which corresponds to the above C++ creation of an object pointed by y. Also in Java y is a reference to an object, not the object itself. However, Java does not allow the typical operations supported by C++ on pointers, like pointer arithmetic, addressing operator, etc.

The applicatioon of a method to y, in Java, is done as following:

   y.f();
Another difference is that Java does not support call by reference, only call by value. However, when the parameter is an object, the changes made by the callee will be transmitted to the caller, because the actual parameter really stands for the reference to the object. The situation is similar to passing a pointer by value in C or in C++.

Definition of threads in Java

A thread manages a single sequential thread of control. In Java, threads may be created and destroyed dynamically.

The typical way of creating a thread is as an object of a class based on a special java class, called "Thread". This class is characterized by two special methods, run() and start(). The former has an empty definition in Thread, and it is meant to be overriden in the derived classes. The latter starts the thread, namely tells the JVM to spawn a new thread and to activate it. When the JVM activates the new thread, it runs its method run(), thus making the thread active. The actual code executed depends of course on the implementation provided for run() in the derived class.

   class MyThread extends Thread {
        public void run() {
              //...
        }
   }

                     ------------------
                     |     Thread     |
                     ------------------
                     |                |
                     |     run()      |  
                     |                |
                     ------------------
                             ^
                             |
                             |
                     ------------------
                     |    MyThread    |
                     ------------------
                     |                |
                     |     run()      |
                     |                |
                     ------------------


Life-cycle of a thread in Java

In Java a thread has three principal states:

The substates of an alive thread

An alive thread has three possible substates

Example

The following program prints the words ``ping'' and ``pong'' at different rates
public class PingPong extends Thread {
    private String word;  // what word to print
    private int delay;    // how long to pause (in milliseconds)
  
    public PingPong(String whatToSay, int delayTime) {
        word = whatToSay;
        delay = delayTime;
    }

    public void run() {
        try {
            for (;;) {
                System.out.print(word + " ");
                sleep(delay);   // pause before printing next word
            }
        } catch (InterruptedException e) {
            return;   // end this thread
        }
    }

    public static void main(String[] args) {
        new PingPong("ping",  33).start();  // 1/30 second
        new PingPong("PONG", 100).start();  // 1/10 second
    }
}

Possible effect of an execution of PingPong.main:
   ping PONG ping PONG ping ping PONG ping ping ping 
   PONG ping ping ping PONG ping ping PONG ping ping 
   ping PONG ping ping PONG ping ping ping PONG ping 
   ping PONG ping ping ping PONG ping ping PONG ping
   ...
Different execution may give different results. The interleaving (and interaction) between threads depends on many factors and it is in general unpredictable.