510 likes | 641 Views
Guarded Methods and Waiting. Reminder!. Concurrency problem: asynchronous modifications to object states lead to failure of thread computation (pre-condition is not met) . Overview. Techniques to deal with potential failures
E N D
Reminder! • Concurrency problem: asynchronous modifications to object states lead to failure of thread computation (pre-condition is not met)
Overview • Techniques to deal with potential failures • check all invariants and preconditions before performing action • refuse to perform actions unless they can ensure that these actions will succeed • exceptions, exception handling code
Policies for failed preconditions/invariants • Balking. throw exception if precondition fails. try, and if you cannot succeed, fail fast • Guarded suspension. suspend method invocation (and thread) until precondition becomes true. • Time-outs. between balking and suspension. Bounded wait for precondition to become true.
Today • guarded suspension • general suspension mechanisms in Java RTE
Guarded suspension and time-outs in concurrent software • abstract queue supports add()get() size()
Guards = special conditionals • Conditionals in sequential programs: • if statement check condition on method entry • if false - no wait - can never become true
Guarded suspension • asynchronous state changes can happen • precondition may hold in the future • wait until precondition holds • guard asserts that another thread will make required state changes… • Time-outs - soft guards - balking policy if wait for too long
Java Mechanics for Guarded Suspension • Wait/Notify constructs - simple, mechanism for suspension in multi-threaded context • mechanism semantics • higher level constructs to simplify usability
wait() • threads may wait() until some other thread wakes them up • each objecthas a wait set - similar to locks • entities possessing locks and wait sets are called monitors • maintained internally by the JVM • set holds threads blocked by wait on object • until notifications are invoked or waits released • threads enter the queue of an object, o, by invoking the wait() method of o. A thread wishing to wakeup threads from the queue of o may call o.notify() (or o.notifyAll()) to wakeup just one thread (resp. all of the threads) waiting in the wait set of o.
notify() • threads enter wait queue of objecto by invoking the wait() of o. • thread wakeups threads from queue of o by:o.notify()/o.notifyAll()
wait/notify cycle • o.wait() - wait invocation • current thread calling wait() is blocked. • JVM places thread in wait set of o. • o.notify() - notify invocation • arbitrarily thread T is removed from o’s wait set • T is resumed from the point of wait(). • o.notifyAll() • like notify except for all threads in o’s wait set
Interrupting Threads • Special JVM exception to threads: • thread may need to stop waiting for a notify() (example when program needs to exit) • InterruptedException – wait() method exits immediately
Back to queue • Producer-Consumer design pattern • Producer produces objects /Consumer needs process • decouple processes that produce and consume data at different rates • queue data in producer loop / process in consumer loop at own pace
video player example: • Producer read the file from disk and give chunks of data to Consumer – add() • Consumer decodes and play – take() • Guard add() / take() methods, such that all preconditions are respected • wait() until the preconditions hold.
thread trying to get() when queue is empty will wait in wait set of queue • thread which successfully add()s an object to queue will wake him up (and vice versa) • What is wrong? • No synchronization
What is wrong? Guard atomicity • two Consumer threads and one producer The • queue is empty at first • consumers execute take() - block (size=0) • producer calls add() once, notifyAll() • both consumers wake up. both execute vec_.remove(0) • precondition does not hold for one of them
What is wrong? Wait atomicity • one producer and one consumer • first runs the consumer, calls take() • consumer checks condition, and is stopped right after (scheduler) • producer calls add() successfully – calls notifyAll() - no thread in wait set at that time • consumer is resumed and calls this.wait() • queue not empty-consumer miss notification.
Solution? • while loop ensures after waking up, checks precondition one more time • no synchronization • use notify() instead of notifyAll() ? dangerous...
Wait Atomicity • ensure no one calls notify() between precondition check and wait. • make both checkand waitatomic with relation to notify mechanism. • use single lock to protect them (?)
Wait Atomicity • thread get hold of a lock • check • wait() - enter a wait set • no other thread may get lock and wake us up!
Wait Atomicity • Java solution: • call to wait() or notifyAll() - calling thread must hold object's monitor (lock). • enter wait() - thread (involuntarily) releases lock • before exit wait() - thread must relock object (why?)
Wait/Notify Mechanism • thread T calls o.wait() • JVM checks that T holds o'slock (if not, exception IllegalMonitorState). • T is blocked, and lock of o is released. • JVM places T in the wait set associated with o.
Wait/Notify Mechanism • thread T calls o.notify() • JVM checks that T holds o's lock. if not exception is thrown. • arbitrarily thread T' is removed from wait set • T' tries to re-acquire the lock of o (T' blocks until it does). • T' is resumed from the point of its wait().
Wait/Notify Mechanism • o.notifyAll() - steps occur simultaneously for all threads in wait set of o. • Thundering Herd phenomena – all threads try to acquire the lock competing with each other. • holding the monitor's lock check is done at runtime and not at compile time.
Rules of Thumb for Wait and Notify • Guarded wait - for each condition that needs to be waited on, write a guarded wait loop that causes the current thread to block if the guard condition is false.
Rules of Thumb for Wait and Notify • Guard atomicity - Condition checks must always be placed in while loops. • When action is resumed, waiting thread doesn't know if condition is true • to maintain safety, it must check again. • thread can wake up without notify, interrupt, or timing out - spurious wakeup.
Rules of Thumb for Wait and Notify • Multiple guard atomicity If there are several conditions which need to be waited for, place them all in the same loop.
Rules of Thumb for Wait and Notify • Don't forget to wake up - ensure liveness, classes must wake up waiting threads. • when value of field in guard changes - waiting threads must be awakened to recheck conditions
Rules of Thumb for Wait and Notify • notifyAll() - multiple threads wait on same wait set: • at least two threads waiting for different conditions, • ensure that no thread misses an event.
notify() Vs. notifyAll(): • queue which has add() /addTwo() methods • Thread t1waits for queue inadd()checking room for one object, • threadt2 waits in addTwo() checking room for two objects. • get() method calls notify() and thread t2 wakes up • room for one object in queue, t2goes back to sleep. • t1 will not be woken up at all, even though there is space in the queue.
Sophisticated Primitives • Wait and Notify: • basis for higher level constructs. • general constructs • Specific mechanisms make code easier to understand and demand less details when using them: • Semaphores • Readers-Writers locks.
Semaphores • Object controls bounded number of permits(permit = train ticket): • Threads ask semaphore for permit (a ticket). • If semaphore has permits available, one permit is assigned to requesting thread. • If no permit available, requesting thread is blocked until permit is available • when a thread returns permit received earlier
Java Semaphore class properties • not re-entrant – thread calls acquire() twice, must release() twice! • semaphore can be released by a thread other than owner (unlike lock) – no ownership. • constructor accepts fairness parameter – if t1 requested before t2 it will receive first • services as tryAcquire and managing permits.
SimpleQueue vs. Semaphore • SQ: add(),remove() call notifyAll() call. • Sem: release() calls notifyAll() method acquire() does not. • SQ:add(), remove() call wait() • Sem: method acquire() calls wait() method release() does not.
SimpleQueue vs. Semaphore • SQ: add(),remove() - non-empty preconditions correspond to 2 distinct states of the Queue: empty or full. • Sem: acquire() has pre-condition and release() has no pre-condition. • release() does not need to call wait(). • acquire() call wait() - symmetric call in release() for notifyAll() (for each wait() - corresponding notifyAll() that unblocks).
Readers/Writers Example • allow readers and writers to access a shared resource under different policies • Policy: • several readers can access resource together • only one writer can access resource at given time • Example: • Readers wait if writer is pending • One writer at a time can access the shared resource, if no readers are reading from it