4/7/09 1 Java Monitors From “Alfonse, Wait Here For My Signal!”, Stephen J. Hartley, SIGCSE 3/99 New Orleans, LA, USA Java Monitors class Monitor extends ... { private ... // data fields (state variables) public synchronized type method1(...) { ... notifyAll(); // if any wait conditions altered while (!condition) try { wait(); } catch (InterruptedException e) {} ... notifyAll(); // if any wait conditions altered } ... }4/7/09 2 Can’t Control Lock Acquisition • The thread blocked the longest on a monitor synchronized method call is not guaranteed to be next in the monitor when the monitor lock is released • The thread blocked the longest on a monitor wait() call is not guaranteed to be the one chosen to reenter the monitor when a notify() is done Barging is possible • The signaling discipline is signal-and-continue so barging is possible: – a thread waiting for the monitor lock so it can call a monitor synchronized method might get the lock before another already waiting thread reacquires the lock. • Usually best to put condition tests in a while loop4/7/09 3 Can’t distinguish different waiters • Each monitor object has a single nameless anonymous condition variable. – we cannot signal one of several threads waiting on a specific condition with a notify() – It is safer to use notifyAll() to wake up all waiting threads – However, this can be very inefficient and can increase overhead Notify Early & Often • A notifyAll() needs to be done by a thread before a wait() if any state variables were altered by the thread after entering the monitor that might affect other thread waiting conditions • This also applies before leaving the monitor (returning from the method).4/7/09 4 Handling InterruptedException • Ignoring InterruptedException with an empty catch block is okay as long as we are using notifyAll() instead of notify(). – Also not okay in using if...wait() • May be better to have the containing method throw the exception back to the method's caller so the latter knows an interrupt occurred A Counting Semaphore • Can be used to allow no more than n threads into a critical section • Has two operations – P (down) • If semaphore’s value is 0 block, else decrement value – V (up) • If one or more threads are blocked inside P then unblock one, else increment the semaphore’s value4/7/09 5 Desired Properties • Safety: No more Ps complete than is possible • Liveness: As many Ps complete as possible • No barging: If threads are waiting in P and V is called, the one thread in P succeeds • Single signals: The V operation is implemented with notify() rather than notifyAll() • Passed back exceptions: If a thread blocked inside P in wait is interrupted, the exception is thrown back to the calling thread 1st Attempt public class CountingSemaphore { private int value = 0; public CountingSemaphore(int initial) { if (initial > 0) value = initial; } public synchronized void P() throws InterruptedException { if (value == 0) wait (); value-- ; } public synchronized void V() { if (value == 0) notify(); value++ ; } }4/7/09 6 1st Attempt • A thread calling P might get in the monitor before a waiting thread reacquires lock • Both P calls will then succeed when only one should, a safety violation • Possible fix: – Change the if in front of the wait to a while 2nd Attempt public class CountingSemaphore { private int value = 0; public CountingSemaphore(int initial) { if (initial > 0) value = initial; } public synchronized void P() throws InterruptedException { while (value == 0) wait (); value--; } public synchronized void V() { if (value == 0) notify(); value++ ; } }4/7/09 7 2nd Attempt • Several threads blocked inside P and some other thread does a V and notify • One of the waiting threads is moved from the wait set to the (re)acquire monitor lock set • Several other threads barge in and call V before the notified thread reenters the monitor • No notifies are done 2nd Attempt • Also have a barging problem that can cause starvation • A thread calling P can barge ahead of a notified thread, causing the notified thread to wait again • Fix: allow semaphore value to go negative, in which case its absolute value is the number of threads blocked inside P in wait4/7/09 8 3rd Attempt public class CountingSemaphore { private int value = 0; public CountingSemaphore (int initial) { if (initial > 0) value = initial; } public synchronized void P() throws InterruptedException { value-- ; if (value < 0) wait(); } public synchronized void V() { value++ ; if (value <= 0) notify(); } } 3rd Attempt • Suppose the semaphore value is -1 due to one thread blocked in wait 0 inside P. Then suppose that thread is interrupted • The value is left at -1 even though no threads are blocked in P • The next V will increment the value to 0 whereas it should now be 1 • Fix: count the wait set separately, rather than letting the semaphore value go negative4/7/09 9 3rd Attempt • Suppose several threads are blocked inside wait and then one of them is notified and then interrupted before it reacquires the monitor lock. • The notify gets “lost” in that one of the other waiting threads should now proceed. • Fix: catch the exception when a thread is interrupted out of wait and regenerate the notify 4th Attempt public class CountingSemaphore { private int value = 0; private int waitcount = 0; public CountingSemaphore(int initial) {if (initial > 0) value = initial;} public synchronized void P() throws InterruptedException { if (value == 0 || waitcount > 0) { waitCount++; try { wait();} catch (InterruptedException e) { notify(); throw e; } finally { waitCount--;} } value-- ; } public synchronized void V() { value++ ; if (waitcount > 0) notify(); } }4/7/09 10 4th Attempt • Suppose a thread blocked inside wait is interrupted without being notified • The notify it does in its catch block will move some other waiting thread, if there is one, out of the wait set into the lock (re)acquire set • When that thread gets back into the semaphore monitor, its P operation will complete successfully, an error • Fix: add a
View Full Document