Condition#signalAll()
only signals threads that are currently in Condition#await()
(or its friends), i.e. the signal isn't queued up for later calls.
You need another flag protected by the lock to correctly implement:
public class Chopstick {
private final Lock lock = new ReentrantLock();
private final Condition chopstickFree = lock.newCondition();
private volatile boolean isFree = true;
Chopstick() { /* Nothing */ }
// Pick up chopstick
public void pickUpChopstick() throws InterruptedException {
lock.lock();
try {
while (!isFree) {
chopstickFree.await();
}
isFree = false;
} finally {
lock.unlock();
}
}
// Release chopstick
public void releaseChopstick() {
lock.lock();
try {
isFree = true;
chopstickFree.signal();
} finally {
lock.unlock();
}
}
}
Here is a version using a Semaphore
that might be a little closer in intent to your original implementation:
public class Chopstick {
private final Semaphore chopsticksAvailable = new Semaphore(1);
Chopstick() {
// Nothing
}
// Pick up chopstick
public void pickUpChopstick() throws InterruptedException {
chopsticksAvailable.acquire();
}
// Release chopstick
public void releaseChopstick() {
chopsticksAvailable.release();
}
}