On Sun, May 31, 2015 at 02:31:25PM +1000, David Holmes wrote:
> >As I recently fell into the trap of forgetting the synchronized block
> >around a single notifyAll(), I believe, the current situation is just
> >errorprone.
>
> How is it errorprone? You forgot to acquire the lock and you got an
> IllegalMonitorStateException when you did notifyAll. That's pointing
> out your error.
The reason for not making wait/notify synchronized is *not* that "it would be
unnecessary because you typically already hold the lock anyway". The reason is
(as David Holms pointed out earlier) that it would be *meaningless* to make
them synchronized.
When you say it's errorprone it sounds like you first had
notifyAll();
and then "fixed" the IllegalMonitorStateException by doing
synchronized (this) {
notifyAll();
}
(and then wrote your original email asking why notifyAll didn't do this "on the
inside").
If this is the case you have not understood the intention of using synchronized
here. In a nutshell wait and notify is all about thread communication, and for
that to work correctly you need to synchronize your execution. Let me try to
explain why wrapping *just* notifyAll() in a synchronized block (or relying on
making notifyAll synchronized) is broken: Suppose you have a producer /
consumer thing going on. In produce() you have something like
enqueue(value);
synchronized (this) {
notifyAll(); // because consumer may be waiting for a value
}
and in consume() you have something like
synchronized (this) {
while (noValueIsAvailable())
wait();
}
value = retrieve();
Suppose now that ProducerThread enters produce(), enqueues a value and before
reaching notifyAll, ConsumerThread comes along, enters consume(), consumes the
value, processes the value, calls consume *again*, sees that noValueIsAvailable
and calls wait. ProducerThread now continues it's execution and calls
notifyAll(). ConsumerThread is woken up, but at this point no value is
available despite there was a call to notify! (In the best case, this doesn't
crash the program, in worst case, ProducerThread assumed that the value should
be processed after notifyAll, in which case you may run into a deadlock. If
there were more variables involved you could also have memory visibility issues
involved and accidentally break class invariants by doing this.)
I've written an answer to a similar question here:
"Why must wait() always be in synchronized block"
http://stackoverflow.com/a/2779674/276052
best regards,
Andreas