The notifyAll() method
An alternative to notify() is notifyAll(). As the name implies, this
method wakes up all threads that are waiting on the given object. So which is
appropriate where?
The notify() method is generally used for resource pools, where
there are an arbitrary number of "consumers" or "workers" that take resources, but when a resource
is added to the pool, only one of the waiting consumers or workers can deal with it.
The notifyAll() method is actually used in most other cases.
Strictly, it is required to notify waiters of a condition that could allow multiple
waiters to proceed. But this is often difficult to know.
So as a general rule,
if you have no particular logic for using notify(), then you should
probably use notifyAll(), because it is often difficult to know exactly
what threads will be waiting on a particular object and why. For example, imagine we have
a fixed-size buffer object. Threads can add objects to the buffer, but must wait if the
buffer is full. The class also has a clear() method to empty the buffer:
public class Buffer {
private List buffer = new ArrayList();
public void addItem(V obj) {
synchronized (buffer) {
while (buffer.size() >= maxSize) {
buffer.wait();
}
buffer.add(obj);
}
}
public void clear() {
synchronized (buffer) {
buffer.clear();
buffer.notifyAll();
}
}
}
Now, it's crucial that the clear() method calls notifyAll() because
it can't tell how many threads are waiting on the buffer, and it performs an action that
can potentially allow various of those threads to proceed.
Generally, if you accidentally call notifyAll() where only notify()
is necessary, this should not cause your program logic to fail: at worst it will cause some
unnecessary context switches as a bunch of threads are in turn woken up, only to find that
the condition they were waiting for has not yet been met, and that they must immediately
wait again. But the converse is not true, as in the Buffer.clear()
method above: here, notifyAll() is necessary because there could be several
threads waiting, and clear() is the only method that does something to
allow them to proceed.
Of course, the semantics of the synchronized lock still apply with notifyAll().
That is to say, each of the awoken threads will, in turn, acquire the lock
and proceed. The next awoken thread 'in the queue' will proceed only after the previous one has released
the lock, either by exiting the synchronized block or by re-entering the wait()
method.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.