I've read about the problem on https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html.
This is what the documentation says about the two conditions:
We would like to keep waiting put threads and take threads in separate wait-sets so that we can use the optimization of only notifying a single thread at a time when items or spaces become available in the buffer. This can be achieved using two Condition instances.
I could notify a single thread using one condition since signal()
wakes up one thread:
class BoundedBuffer2 {
final Lock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
condition.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
condition.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
condition.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
condition.signal();
return x;
} finally {
lock.unlock();
}
}
}
Why do we need two conditions?
Say your buffer was empty and two threads were waiting on take
, blocked on
condition.await();
Then a thread calls put
. This will signal
the Condition
and wake up one of the take
threads. That take
thread will subsequently also signal
the Condition
, waking up the other take
thread which will loop back in blocking state since there is nothing for it to take.
That last signal
was unnecessary. That second take
thread should never have been woken up since nothing was available.
The two condition algorithm allows you to notify only those threads that are relevant.