0

I am trying to understand what is the use of doing condition.await() if I am already doing lock.lock() If I understood locks correctly, once I do lock.lock() it will not proceed any further if some other thread has a lock.

So, in this case if pushToStack() has acquired a lock by doing lock.lock() then what is the use of checking for stackEmptyCondition.await() in the popFromStack() method? Because anyway, the code will stop at the lock.lock() line in the popFromStack() method. What am I missing/wrong?

public class ReentrantLockWithCondition {

    Stack<String> stack = new Stack<>();
    int CAPACITY = 5;

    ReentrantLock lock = new ReentrantLock();
    Condition stackEmptyCondition = lock.newCondition();
    Condition stackFullCondition = lock.newCondition();

    public void pushToStack(String item){
        try {
            lock.lock();
            while(stack.size() == CAPACITY) {
                stackFullCondition.await();
            }
            stack.push(item);
            stackEmptyCondition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public String popFromStack() {
        try {
            lock.lock(); // we are blocked here to acquire a lock
            while(stack.size() == 0) {
                stackEmptyCondition.await(); // then why do we need to check this again?
            }
            return stack.pop();
        } finally {
            stackFullCondition.signalAll();
            lock.unlock();
        }
    }
}
figaro
  • 2,263
  • 3
  • 20
  • 27
  • `await()` releases the lock until it gets the signal. – shmosel May 13 '22 at 23:43
  • 1
    The idea is: If a thread wants to push to a full stack and obtains the lock, then what is it supposed to do? It needs to wait until the stack is no longer full. That condition can only occur when another thread comes along and pops an element from the stack. But that means the "pop thread" needs to acquire the lock, so the thread waiting to push an element needs to release the lock while waiting for the stack to no longer be full. It does that by calling `await()`. Then the "pop thread" signals that condition, waking up the waiting "push thread" so that it can continue doing what it was doing. – Slaw May 14 '22 at 00:56
  • 1
    And the same in the opposite direction (when a thread tries to pop an element but the stack is empty). Note when a thread wakes up from `await()` it has reacquired the lock. – Slaw May 14 '22 at 00:57
  • 1
    TLDR: Use a lock to control access to shared data. Use a condition variable to let one thread notify another about a change in shared data. – Solomon Slow May 14 '22 at 14:15

1 Answers1

0

The point is the Condition, not the Lock.

It is often the case that a program needs to wait until either "something happens" or "something is in a particular state". The Condition represents what you're waiting for.

In order to program such a thing safely, some sort of locking is needed. If you're waiting for something to be in a particular state, you really want it to remain in that state while you do whatever you had in mind when you decided to wait for it. That's where the Lock comes in.

In your example, you want to wait until the stack is not full, and when you discover that the stack is not full, you want it to stay not-full (that is, prevent some other thread from pushing on to the stack) while you push something on that stack.

dangling else
  • 438
  • 2
  • 3