Originally posted by Greg Charles:
The use of a while loop for wait() is generally a good practice. [...] Personally, I always use notifyAll() instead of notify(). My sense is that method works better to prevent starvation and deadlocks, but I can't really explain why I think that.
As you mention, the use of if() or while() is actually related to the use of notify() vs notifyAll(). Maybe I can help clarify matters.
You use notify() when the waiting threads are all interchangeable. Because if and only if they are fully interchangeable you don't care which specific thread gets woken up. They can all do the job. A good example would be a thread pool; one or more Threads which wait() until a Runnable job is posted in a job queue. Every single one of the waiting Threads will be able to handle your job, so you can simply call notify() and be sure that the job gets done.
You use an if(condition) as opposed to a while(condition) loop when after notification the wait condition is guaranteed to be met. This can only be the case when the waiting threads are all interchangeable. Imagine a thread pool with a single worker thread. When it's woken up, you
know that this is because a job has been posted to the queue:
Why bother with the if()? Because if notify() has been called while the thread was busy, the notify will be "lost". By picking up any waiting jobs that are in the queue, such jobs will still be processed.
Do not assume that above statement is true the other way around -- the fact that all waiting threads are interchangeable does not automatically mean you can use if() instead of while(). For example, things get subtly different when there are multiple threads all consuming jobs from the same queue. They are interchangeable, so you can still use notify(), but there is no longer any guarantee that the wait condition will be met when a thread wakes up.
I will leave the reason why as an exercise for the reader. Hint: it involves a race condition between a woken-up waiting thread and another thread that has just finished executing its job.
You use notifyAll() when waiting threads are not interchangeable. If some of the waiting threads are able to handle the condition, but others aren't, then you have no choice but to wake up all of them. Imagine, for example, that you want to limit the queue size in the thread pool; this means that a thread that posts new jobs may have to wait() until there is room in the queue. This means you suddenly have
two flavours of waiting threads: job posters, and job runners. A call to notify() when there's a free place in the queue may wake up either a poster or a runner. That's useless. You specifically want a poster; the only way to achieve this is to wake up everyone and have every thread check its condition. Which seamlessly leads us into...
You use a while(condition) loop when there is no guarantee that after notification the condition is met. This is generally the case when the waiting threads are not interchangeable. In the example, a notify may mean that there's a job waiting to be run or, conversely, that there's a free place in the queue to post a job to. And since you're forced to wake up all waiting threads, multiple runners will compete for a single job and only one will get it. So all will need to check their wait condition:
So what should you use? notify() or notifyAll()? An if() or a wait()? I am personally strongly in favour of
not using a single method to cover all situations. Your implementation sends a message to the reader of your code. If you use a while(condition) loop, he will understand that when a thread is woken up the wait condition may not be met --- and if you do this in a situation where, in fact, it
is always met you will have confused the reader with misleading code. Made your code that little bit less maintainable.
Hope this helps.
- Peter
[ February 07, 2003: Message edited by: Peter den Haan ]