Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 signal(), but if only one thread at a time is notified, how can this go wrong?

Their code here - check out the put() and take() methods; a simpler and more-to-the-point implementation can be seen at the top of the JavaDoc for Condition.

Relevant portion of my implementation below.

public Object get() {
    lock.lock();
    try {
        if( items.size() < 1 )
            hasItems.await();
        Object poppedValue = items.getLast();
        items.removeLast();
        hasSpace.signal();
        return poppedValue; 
    } catch (InterruptedException e) {
        e.printStackTrace();
        return null;
    } finally {
        lock.unlock();
    }
}

public void put(Object item) {
    lock.lock();
    try {
        if( items.size() >= capacity )
            hasSpace.await();
        items.addFirst(item);
        hasItems.signal();
        return;
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        lock.unlock();
    }
}

P.S. I know that generally, particularly in lib classes like this, one should let the exceptions percolate up.

like image 308
kenny_k Avatar asked Jun 02 '10 18:06

kenny_k


1 Answers

To protect against spurious wake ups. There is no guarantee made to you by the JVM that the only possible reason the thread will ever start running again is because you called signal in the way you intended. Sometimes it will just get started accidentally and go (Spurious wake up). So you have to keep waiting again if the condition you want to run on isn't actually true.

This is explained in the javadoc for the wait method: http://java.sun.com/javase/6/docs/api/java/lang/Object.html#wait%28long%29

And mentioned in the docs for await: http://java.sun.com/javase/6/docs/api/java/util/concurrent/locks/Condition.html#await%28%29

The lock associated with this Condition is atomically released and the current thread becomes disabled for thread scheduling purposes and lies dormant until one of four things happens:

  • Some other thread invokes the signal() method for this Condition and the current thread happens to be chosen as the thread to be awakened; or

  • Some other thread invokes the signalAll() method for this Condition; or

  • Some other thread interrupts the current thread, and interruption of thread suspension is supported; or

* A "spurious wakeup" occurs.

Some implementations of the Condition interface may suppress spurious wakeups, but relying on that would hence be relying on an implementation detail and makes your code unportable.

like image 82
Affe Avatar answered Oct 11 '22 19:10

Affe