cs205: engineering software?
(none)
20 September 2010

CS205 Notes 23 (16 October 2006)

Upcoming Schedule

Monitors

Mutual Exclusion

A region of code can be made mutually exclusive by using a synchronization lock. If code is contained inside:

     synchronized (<obj>) { code }
then only one thread can execute the critical code at a time. This means we can reason about the code knowing that no other thread can be running inside a critical section for the same object at the same time.

Wait and Notify

A thread inside a critical section for object obj, can suspend itself using obj.wait(). The wait method is defined by the java.lang.Object class (so it is inherited for all Java objects). Here is its specification (somewhat reworded from the Java API documentation:

public final void wait() throws InterruptedException
   REQUIRES: The current thread owns this object's monitor.
   MODIFIES: The lock associated with this and the calling thread.
   EFFECTS:  Causes the current thread to release the lock associated
      with this.  The current thread will wait until it reobtains the
      lock, which can happen after another thread invokes the 
      notify() or notifyAll() method for this object.  Adds the current
      thread to the waiting set associated with this.  If this thread
      is interrupted while waiting, throws InterruptedException.
The notify method is used to wake up a waiting thread:
public final void notify()
   REQUIRES: The current thread owns this object's monitor.
   MODIFIES: The lock associated with this, and its waiting set.
   EFFECTS: If any threads are waiting on this object, one of those
      threads is choosen to be awakened.  That thread is removed
      from the waiting set of this and placed in the set of threads
      waiting to be scheduled when the lock on this is available.
Note that the current thread does not release the object's lock when it calls notify. It will retain the lock until it is released (for example, but calling wait or exiting the synchronized block).

The notifyAll method is used to wake up all threads waiting on this object:

public final void notifyAll()
   REQUIRES: The current thread owns this object's monitor.
   MODIFIES: The lock associated with this, and its waiting set.
   EFFECTS: Moves all threads in the waiting set of this object 
      into the set of threads to be scheduled when the lock on 
      this is available.
The difference between notify and notifyAll is that notify chooses one of the threads that is waiting on this object and allows it to be scheduled, whereas notifyAll allows all of the threads waiting on this to be scheduled.

Is it possible for a thread to simultaneously be in the wait set for more than one object?






Dining Philosophers

philosophers.zip (contains all code)

Chopstick.java

public class Chopstick {
   private Object holder;
   private int no;
   
   public Chopstick(int n) { no = n; }
   
   public int getNumber() { return no; }
   
   public boolean isHeld() {
      return holder != null;
   }
   
   public boolean take(Object h) {
      synchronized(this) {
         if (holder == null) {         
            holder = h;
            System.out.println("Chopstick " + toString() 
                 + " picked up by " + holder);
            return true;
         } else {
            return false;
         }
      }
   }
   
   public void release() {
      synchronized(this) {
         if (holder != null) {
            System.out.println("Chopstick " + toString() 
                 + " released by " + holder);
            holder = null;
         }
         notify();
      }
   }
}

Philosopher.java

public class Philosopher implements Runnable {
   protected String name;
   protected Chopstick left;
   protected Chopstick right;
   
   public Philosopher(String p_name, Chopstick p_left, Chopstick p_right) {
      name = p_name;
      left = p_left;
      right = p_right;
   }
   
   public String toString() {
      return name;
   }
   
   protected void philosophize() {
      System.out.println (name + " ponders the meaning of threads.");
      try {
         Thread.sleep(1000);
      } catch (InterruptedException ie) { 
         ;
      }
   }
   
   protected void eat() {
      System.out.println(name + " eats.  Yum Yum.");
      try {
         Thread.sleep(1000);
      } catch (InterruptedException ie) { 
         ;
      }
   }
   
   public void run() {
      while (!left.take(this)) { 
         philosophize();
      }
      
      philosophize();
      
      while (!right.take(this)) {
         philosophize();
      }
      
      eat ();
      left.release();
      right.release();
      System.out.println(name + " is finished.");
   }
}

Main.java

package philosophers;

public class Main {
   static private final String [] philos = 
       { "Aristotle", "Boole", "Comte", "Decartes", "Einstein" };
   static private final int numPhilosophers = philos.length;
   
   public static void main(String args[]) {
      Philosopher [] philosophers = new Philosopher[numPhilosophers];
      Chopstick [] chopsticks = new Chopstick[numPhilosophers];
      
      for (int i = 0; i < numPhilosophers; i++) {
         chopsticks[i] = new Chopstick (i);
      }
      
      for (int i = 0; i < numPhilosophers; i++) {
         philosophers[i] = new Philosopher (philos[i], chopsticks[i], 
                                            chopsticks[(i + 1) % numPhilosophers]);
      }
      
      // Start all philsopher threads
      for (int i = 0; i < numPhilosophers; i++) {
         System.out.println("Starting philosopher: " + philosophers[i]);
         new Thread(philosophers[i]).start();
      }
   }
}
Explain how the above code could deadlock?













Modify the run method to avoid the deadlock (without removing the philosophizing between each action).

   public void run() {
      Chopstick first, second;
      if (left.getNumber() < right.getNumber()) {
         first = left;
         second = right;
      } else {
         first = right;
         second = left;
      }
      synchronized (first) {
         while (first.isHeld ()) {
            try {
               first.wait();
            } catch (InterruptedException e) {
               ;
            }
         }
         first.take(this);
      }
      philosophize();
      
      synchronized (second) {
         while (second.isHeld()) {
            try {
               second.wait();
            } catch (InterruptedException e) {
               ;
            }
         }
         second.take(this);
      }
   
      eat ();
      first.release();
      second.release();
      System.out.println(name + " is finished.");
   }
Why can't we just do:
      synchronized (first) {
         if (first.isHeld ()) {
            try {
               first.wait();
            } catch (InterruptedException e) {
               return;
            }
         }
         first.take(this);
      }





Given all their dining problems, it shouldn't surprise you that philosophers also have some Drinking Problems (http://www.cs.utexas.edu/users/misra/scannedPdf.dir/DrinkingPhil.pdf). It may be more surprising, however, that cryptographers also have a dining problem (http://www.totse.com/en/privacy/encryption/chaum.html).