I have been playing with my own version of this, using \'if\', and all seems to be working fine. Of course this will break down horribly if signalAll() is used instead of s
Why does java.util.concurrent.ArrayBlockingQueue use 'while' loops instead of 'if' around calls to await()?
They use while
instead of if
to protect against the classic thread race conditions in producer/consumer models and to protect against the much more rare case of spurious wakeups.
When (for example) multiple consumers are waiting for a particular condition (like a queue is empty) and the condition gets notified, there is a chance that another thread may lock first and "steal" the item added to the queue. The while
loop is necessary for the thread to make sure that the queue has an item before it tries to de-queue it.
I've written some sample code and more documentation which demonstrates the race condition.
Looking at your specific code, the race is as follows:
await()
in the while loop, waiting for there to be items in the queueget()
, locks on the queue, and has to wait for #2 to unlock (it is NOT waiting on hasItems
but it is waiting to get the lock
)hasItems.signal()
to notify someone that there is an item thereif
statement, it would go forward and try to dequeue from an empty list which would throw ArrayIndexOutOfBoundsException
or something.The reason why the while
statement is necessary is to handle these race conditions. In step 8 above, with a while
, thread #1 would instead loop around back to the test to see if there are items in the queue, finds out that there are none, and then goes back to waiting.
This is a classic issue that trips up a lot of reentrant programmers. The initial versions of the O'Reilly pthreads bible, for example, had example code without the while loop and had to be republished.
With some thread systems, it is easier for the system to awaken all conditions instead of the specific condition that has been signaled so a "spurious wakeup" can occur. The while
loops protect against this as well.