Why does java.util.concurrent.ArrayBlockingQueue use 'while' loops instead of 'if' around calls to await()?

前端 未结 5 863
面向向阳花
面向向阳花 2020-11-30 07:56

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

5条回答
  •  攒了一身酷
    2020-11-30 08:29

    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.

    Sample Code

    I've written some sample code and more documentation which demonstrates the race condition.

    Description of the Race Condition

    Looking at your specific code, the race is as follows:

    1. thread #1, a consumer, is in await() in the while loop, waiting for there to be items in the queue
    2. thread #2, a producer, locks on the queue
    3. thread #3, a consumer, finishes consuming the last item, calls get(), 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)
    4. thread #2, adds an item into the queue and calls hasItems.signal() to notify someone that there is an item there
    5. thread #1 is awoken and goes to lock the queue, has to wait for #2 to unlock
    6. thread #2 unlocks
    7. thread #3 is ahead of thread #1 waiting for the lock so it locks the queue first, goes into the while loop and dequeues the item that #1 was notified for, and then unlocks
    8. thread #1 now is able to lock. If it was just an if 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.

提交回复
热议问题