Wait, Notify and NotifyAll
Busy
Wait :
A thread continuously polling to see if it has some task to do is
called to be on busy wait. Busy wait can be prevented by using
wait/notify(all) from inside a synchronized block.
Example:
// A dummy object.
// Used only for wait/notify purposes.
public class MonitorObject
{
}
// Class actually using wait/notify
public class MyWaitNotify
{
MonitorObject myMonitorObject = new MonitorObject();
public void doWait() {
synchronized(myMonitorObject) {
try{
myMonitorObject.wait();
} catch(InterruptedException e) {...}
}
}
public void doNotify() {
synchronized(myMonitorObject) {
myMonitorObject.notify();
}
}
}
notify
wakes-up any one thread waiting on that object.
notifyAll
wakes-up all the threads waiting on that object.
All
the 3 methods must be called from inside a synchronized block.
But,
how is this possible?
Wouldn't
the waiting thread keep the lock on the monitor object
(myMonitorObject) as long as it is executing inside a synchronized
block? Will the waiting thread not block the notifying thread from
ever entering the synchronized block in doNotify()?
The
answer is no. Once a thread calls wait() it releases the lock it
holds on the monitor object before going into wait state. This allows
other threads to call wait() or notify() too, since these methods
must be called from inside a synchronized block.
Once
a thread is awakened it cannot exit the wait() call until the thread
calling notify() has left its synchronized block. In other words: The
awakened thread must re-obtain the lock on the monitor object before
it can exit the wait() call, because the wait call is nested inside a
synchronized block. So if multiple threads are awakened using
notifyAll() only one awakened thread at a time can exit the wait()
method, since each thread must obtain the lock on the monitor object
in turn before exiting wait().
Spurious
Wakeups
It
is possible for a thread to wake-up even without a notify call due to
inexplicable reasons.
If
such a thing happens, it can devastate the application.
Such
a thing can be prevented by using an auxiliary variable to indicate
the state of notification.
Example:
Difference
between notify() and notifyAll()
notify()
causes one thread to wake-up and run while notifyAll() causes all
threads to wake-up but since the woken-up threads will need to
acquire the lock (as they are inside synchronized block), only one
thread will run here too.
Difference
between the two is that after notify, only one thread is in
non-waiting state, rest all are still waiting. Whereas in notifyAll,
all threads come out of the waiting state and do not need another
notify or notifyAll call to start them.
Difference
between wait, lock and synchronized
Synchronized
is same as lock except that it is cleaner but offers less flexibility
by blocking access to a critical section of code.
Lock
means that only one thread can acquire lock, all other threads
calling lock() will block if the lock was already taken by some
thread. So synchronized is basically a special form of Lock. With
synchronized, you do not have to worry about locks not getting
released due to exceptions in critical section.
Wait()
means the calling thread goes to waiting state and gives up the lock
it’s holding before going to waiting state. When it will come
alive, it will first try to acquire the lock it was holding before
proceeding ahead. If the current thread does not own the lock on the
object on which it called wait, an lllegalMonitorStateException is
thrown.
This
means that wait can be called only after acquiring lock on an object.
Purpose of wait is to allow other threads to enter the critical
section (a section guarded by locks) by giving up the lock till
notify is called.
That’s
why when the waiting thread gets notified, it tries to get the lock
before proceeding because the wait call happens in critical region.
So,
in summary,
lock/synchronized:
Take a lock before proceeding. If lock taken already, wait.
wait:
Give up the lock taken already and wait on some object to be notified
by some other thread for proceeding.
A
thread waiting on a lock is called Blocked
A
thread waiting on an object’s monitor (due to wait()) is called
Waiting
So,
a thread is made to go into ‘blocked’ state due to action
of some other thread (which took the lock before this thread). While
a thread goes into ‘waiting’ state voluntarily and gives
other ‘blocked’ threads a chance to run by releasing the
lock.
Aid
to remember:
Block state happens when attempting to lock (Block and lock rhyme).
Waiting state happens when thread itself calls wait (Waiting and wait
- same prefix)
|