cs205: engineering software? |
(none) 20 September 2010 |
Upcoming Schedule
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?
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(); } } }
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."); } }
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).