Lecture 17:

Contents of today's lecture:

Next
An example
Previous Next
A few words about Java applets
  • A Java applet is run by the Java interpreter inside a browser

  • The execution of an applet can be activated from an html document by an APPLET tag like
       <APPLET CODE = "MyApplet.class">
           <PARAM NAME = ... VALUE = ...>
       </APPLET>
    

  • MyApplet should be a subclass of the class Applet (MyApplet.class is the result of the compilation of the program declaring such class, MyApplet.java)

  • The browser downloads MyApplet.class and creates an object of class MyApplet. Then it invokes the method init(), and then start(). While running, the applet may cause other classes to be downloaded.

  • The class Applet has four special methods that are "recognized" by the browser and that define the lifecycle of an object: init(), start(), stop() and destroy(). Usually they are overriden in MyApplet.

    • init() is called the first time the page is visited
      • initializes the object, typically using the parameters
      • creates the thread(s), if any

    • start() is called each time the page is visited
      • starts the threads (the first time)
      • resumes the threads (the other times)

    • stop() is called each time we leave the page
      • suspends the threads

    • destroy() is called when we leave the page definitively
      • frees up any resource
      • terminates the threads
Previous Next
Using threads in applets
  • There is no multiple inheritance in Java, hence it is not possible to define a class which is at the same time an extension of Thread and of Applet

  • If we need to run a thread in the applet, one way is to define MyApplet as an extension of Applet and an implementation of the interface Runnable.
       public class MyApplet extends Applet 
                             implements Runnable {...}
    
    Then we can create a thread with the statement
       new Thread(this)
    
  • More in general, we can create a thread by using the Thread constructor with a Runnable parameter r, When we start the thread, the method run() executed is the one of r.
     ________          __________          __________           __________
    |        | target |          | implmt |          | extends |          |
    | Thread |------->| Runnable |<.......|  MyRunbl |-------->| supercls |      
    |________|        |__________|        |__________|         |__________|
                      |          |        |          |
                      |   run()  |        |   run()  |
                      |__________|        |__________|
    
                                               r        
    

    This is an alternative way of creating threads in Java, and it is often preferable to the one we have seen previous time (threads as extensions of Thread), because:

    • more flexible (MyRunbl can be an extension of another class)

    • if we need only run(), then inheriting all methods of Thread is an unnnecessary overhead
Previous Next
Semaphores
  • One of the first mechanisms for process synchronization; proposed by Dijkstra in 1968

  • A semaphore s is a non-negative integer variable on with two operations defined on it:

    • P ("passeren", Dutch for "to pass") : if s>0 then s = s-1

    • V ("vrijgeven", Dutch for "to release") : s = s+1

  • Semaphores can be used to ensure that only a certain number of processes can execute a certain critical section at the same time.

  • A process tries to execute a P on s before entering its critical section. If the P cannot be executed because s is 0, then it waits

  • After leaving its critical section a process executes a V on s. At this point one of the processes waiting on s will be resumed. Often (but not necessarily) the mechanism follows a FIFO discipline.
The example
Previous Next
Semaphores: implementation in Java
  • We will use methods up() and down() to represent the V and the P respectively
   public class Semaphore {
       private int value;

       public Semaphore(int initial) {
           value = initial;
       }

       synchronized public void up() {
           ++value;
           notify();
       }

       synchronized public void down() 
               throws InterruptedException {
           while (value == 0) wait;
           --value;
       }
   }
The example
Previous Next
The program SemaDemo.java
package concurrency.semaphore;

import java.awt.*;
import java.applet.*;
import concurrency.display.*;

/********************************************************/

class MutexLoop implements Runnable {

    Semaphore mutex;

    MutexLoop (Semaphore sema) {mutex=sema;}

    public void run() {
      try {
        while(true)  {
           while(!ThreadPanel.rotate());
            // get mutual exclusion
            mutex.down();
            while(ThreadPanel.rotate()); //critical section
            //release mutual exclusion
            mutex.up();
        }
      } catch(InterruptedException e){}
    }
}

/********************************************************/

public class SemaDemo extends Applet {

    ThreadPanel thread1;
    ThreadPanel thread2;
    ThreadPanel thread3;
    NumberCanvas semaDisplay;

    public void init() {
        setLayout(new BorderLayout());
        semaDisplay = new NumberCanvas("Mutex");
        add("Center",semaDisplay);
        Panel p = new Panel();
        p.add(thread1=new ThreadPanel("Thread 1",Color.blue,true));
        p.add(thread2=new ThreadPanel("Thread 2",Color.blue,true));
        p.add(thread3=new ThreadPanel("Thread 3",Color.blue,true));
        add("South",p);
    }

    public void start() {
        Semaphore mutex = new DisplaySemaphore(semaDisplay,1);
        thread1.start(new MutexLoop(mutex));
        thread2.start(new MutexLoop(mutex));
        thread3.start(new MutexLoop(mutex));

    }

    public void stop() {
        thread1.stop();
        thread2.stop();
        thread3.stop();
    }
}

The example
Previous Next
Safety and Liveness properties
  • Safety: "nothing bad will happen" (the program will never reach a bad state)

  • Liveness: "something good will eventually happen" (the program will eventually reach a good state)

Safety and liveness properties can be characterized precisely by using Temporal Logic. They corresponds to a special format of T.L. formulae. However, for our purposes we will just use the above intuitive description

These general principles are relevant also in sequential programming, but in concurrent programming there are far many more properties that need to be satisfied in order for a program to be considered "correct"

Previous Next
Safety
Examples of safety properties
  • Sequential programming

    • non-violation of operation restrictions on data types (e.g. division by 0, head of an empty list, etc.)

    • correct input-output behavior (most general safety property in sequential programming?)
  • Concurrent programming

    • non-violation of operation restrictions

    • mutual exclusion of critical sections

    • deadlock-freedom

    • ...
Previous Next
Liveness
Examples of liveness properties
  • Sequential programming

    • termination
  • Concurrent programming

    • fairness

    • starvation-freedom

    • livelock-freedom

    • ...

      Usually termination is not an issue: interactive computations are "naturally" infinite
Previous Next
Deadlock
Deadlock: each process is blocked waiting for a resource held by another process. "Deadly embrace"

  • Coffman, Elphick and Shoshani identify the following necessary and sufficient conditions for deadlock to occur:

    1. Serially reusable resources: the process involved share resources that they use under mutual exclusion

    2. Incremental acquisition: processes hold on to resources already allocated to them while waiting to acquire additional resources

    3. No preemption: once acquired by a process, resources cannot be peempted (forcibly withdrawn); they can only be released voluntarily

    4. Wait-for cycle: a circular chain of processes exists such that each process holds a resource which is successor in the cycle is waiting to acquire

  • Avoiding deadlock (and livelock) is in general a difficult problem. Note that even proving deadlock-freedom is difficult (undecidable).

  • A practical method which works well, in many cases, is to establish an ordering on the resources and oblige the processes to acquire the resources according to that order. In this way we avoid Condition 4 (cycle).
Previous Next
A typical example of deadlock problem: the dining philosophers
The problem was introduced by Dijkstra in 1968 Five philosophers sit around a circular table. Each philosopher spends his life alternatively thinking and eating. In the centre of the table is a large plate of spaghetti. A philosopher needs two forks to eat spaghetti. Unfortunately, the philosophers have only five forks. Each fork is placed between a pair of philosophers and each philosopher can only use the forks to his immediate right and left.
class Philosopher extends Thread {
  private int identity;
  private PhilCanvas view;
  private Diners controller;
  private Fork left;
  private Fork right;

  Philosopher(Diners ctr, int id, Fork l, Fork r) {
    controller = ctr; view = ctr.display;
    identity = id; left = l; right = r;
  }

  public void run() {
    try {
      while (true) {
        //thinking
        view.setPhil(identity,view.THINKING);
        sleep(controller.sleepTime());
        //hungry
        view.setPhil(identity,view.HUNGRY);
        right.get();
        //gotright chopstick
        view.setPhil(identity,view.GOTRIGHT);
        sleep(500);
        left.get();
        //eating
        view.setPhil(identity,view.EATING);
        sleep(controller.eatTime());
        right.put();
        left.put();
      }
    } catch (java.lang.InterruptedException e) {}
  }
}
Dining philosophers demo
Previous Next
Solutions to the problem of deadlock in the dining philosophers
There are several solutions, but essentially we can classify them in three categories

  1. Centralization
    • additional process that control the activities of the philosophers
    • n-1 tickets (n is the number of the philosophers)
    • semaphore
    • monitor on the forks
    • ...

  2. Breaking the symmetry
    • passing around n-1 tokens (Chandy and Misra, 1984)
    • ordering on the forks
    • ordering on the philosophers
    • ...

  3. Randomization Lehman and Rabin proved (1981) that there is no deterministic, distributed, symmetric, deadlock-free solution to the problem of dining philosophers. They proposed a randomized solution, with all the above properties except determinism.
    Each philosopher flips a coin before choosing the fork. Once he has acquired the first fork he looks for the other fork. If the latter is not available, then he releases the first fork
    To be more precise, in this solution it is still possible that no philosopher ever gets to eat, but this situation has probability 0.
    Previous Next
    A deadlock-free solution to the dining philosophers
    This solution is of the kind "breaking the symmetry". Deadlock is avoided by making even numbered philosophers pick up their left chopsticks first, and odd numbered philosophers pick up their right chopsticks first. Note that this solution guarrantees that some philosopher will eventually eat. Note that this solution does not guarrantee starvation-freedom (for all philosophers): some philosopher may never get to eat because his neighbours may be always "faster" to get the shared chopsticks.
    class FixedPhilosopher extends Thread {
    
        int identity;
        boolean stopRequested = false;
        PhilCanvas view;
        Diners controller;
        Fork left;
        Fork right;
    
        FixedPhilosopher(Diners controller, int identity, Fork left, Fork right) {
            this.controller = controller;
            this.view = controller.display;
            this.identity = identity;
            this.left = left;
            this.right = right;
        }
    
        public void run() {
            while (!stopRequested) {
                 try {
                    //thinking
                    view.setPhil(identity,view.THINKING);
                    sleep(controller.sleepTime());
                    //hungry
                    view.setPhil(identity,view.HUNGRY);
                    //get forks
                    if (identity%2 == 0) {
                        left.get();
                        view.setPhil(identity,view.GOTLEFT);
                    } else {
                        right.get();
                        view.setPhil(identity,view.GOTRIGHT);
                    }
                    sleep(500);
                    if (identity%2 == 0)
                        right.get();
                    else
                        left.get();
                    //eating
                    view.setPhil(identity,view.EATING);
                    sleep(controller.eatTime());
                    right.put();
                    left.put();
                 } catch (java.lang.InterruptedException e) {}
            }
        }
    
        public void stopRequested() {
            stopRequested = true;
        }
    }
    
    DF and LF dining philosophers demo
    Previous Next
    Fairness
    • Weak fairness An action which is always enabled will eventually be executed (a process which is always ready will eventually be scheduled for execution).

      More precisely, there are no infinite chains of "enabled" states for the same action

      blocked -> blocked -> enabled -> enabled -> enabled -> executed
    • Strong fairness An action which is enabled infinitely often will eventually be executed (a process which is ready infinitely often will eventually be scheduled for execution)

      More precisely, there are no infinite chains in which the state "enabled" for the same action is present infinitely many times, and "executed" is present only finitely many times.

      blocked -> enabled -> blocked -> enabled -> blocked -> enabled -> executed

    Note that the DF, LF solution to the dining philosophers seen before is not strongly fair. Strong fairness is very difficult to achieve.

    Previous Next
    An example of fairnes problem: the single-lane bridge
    In the middle of a road there is a single-lane bridge. Cars coming from the left are red and cars coming from the right are blue. Only cars of the same color (i.e. going in the same direction) are allowed on the bridge at each time. The problem of fairness consists in ensuring that each car sooner or later will pass the bridge. Note that it's a problem of strong fairness
    class RedCar implements Runnable {
        ...
        public void run() {
          try {
            while(true) {
                while (!display.moveRed(id));  // not on bridge
                control.redEnter();
                while (display.moveRed(id));   // move over bridge
                control.redExit();
            }
          } catch (InterruptedException e){}
        }
    }
    
    class BlueCar implements Runnable {
        ...
        public void run() {
          try {
            while (true) {
                while (!display.moveBlue(id));  // not on bridge
                control.blueEnter();
                while (display.moveBlue(id));   // move over bridge
                control.blueExit();
             }
          } catch (InterruptedException e){}
        }
    }
    
    class SafeBridge extends Bridge {
    
        private int nred  = 0;
        private int nblue = 0;
    
        synchronized void redEnter() throws InterruptedException {
            while (nblue>0) wait();
            ++nred;
        }
    
        synchronized void redExit(){
            --nred;
            if (nred==0)
                notifyAll();
        }
    
        synchronized void blueEnter() throws InterruptedException {
            while (nred>0) wait();
            ++nblue;
        }
    
        synchronized void blueExit(){
            --nblue;
            if (nblue==0)
                notifyAll();
        }
    }
    
    Single-lane bridge demo
    Previous Next
    A fair solution to the single-lane bridge
    • A boolean variable keeps trace of whose turn it is (blue or red). The turn is alternated

    • A car of color C can enter only if
      • there are no car of the other color on the bridge, and
      • it is C's turn, or there are no cars of the other color waiting

    class FairBridge extends Bridge {
    
        private int nred  = 0;
        private int nblue = 0;
        private int waitblue = 0;
        private int waitred = 0;
        private boolean blueturn = true;
    
        synchronized void redEnter() throws InterruptedException {
            ++waitred;
            while (nblue>0 || (waitblue>0 && blueturn)) wait();
            --waitred;
            ++nred;
        }
    
        synchronized void redExit(){
            --nred;
            blueturn = true;
            if (nred==0)
                notifyAll();
        }
    
        synchronized void blueEnter()  throws InterruptedException {
            ++waitblue;
            while (nred>0 || (waitred>0 && !blueturn)) wait();
            --waitblue;
            ++nblue;
        }
    
        synchronized void blueExit(){
            --nblue;
            blueturn = false;
            if (nblue==0)
                notifyAll();
        }
    }
    
    Previous