Search code examples
javamultithreadingconcurrencywaitjava.util.concurrent

Why Lock condition await must hold the lock


I am in doubt with that , in Java language, we need to acquire the lock, before we await some condition to be satisfied.

For example, int java monitor lock:

synchronized(lock){
   System.out.println("before lock ...");
   lock.wait();
   System.out.println("after lock ...");
}

or the concurrency utils:

Lock lock = new ReentrantLock();
Condition cond = lock.newCondition();

lock.lock();
try{
     System.out.println("before condition ...");
     cond.await();
     System.out.println("after condition ...");
}catch(Exception e){
     e.printStackTrace();
}finally{
     lock.unlock();
}

So, why we can't await, without hold the lock ?

Does other languages differ, or it's just in Java?

I hope you can explain the reason after the design, but not only for JAVA-SPEC definition.


Solution

  • Imagine you have something that a thread might need to wait for. Maybe you have a queue and a thread needs to wait until there's something on the queue so it can process it. The queue must be thread-safe, so it has to be protected by a lock. You might write the following code:

    1. Acquire the lock.
    2. Check if the queue is empty.
    3. If the queue is empty, wait for the something to be placed on the queue.

    Oops, that won't work. We hold the lock on the queue so how can another thread place something on it? Let's try again:

    1. Acquire the lock.
    2. Check if the queue is empty.
    3. If the queue is empty, release the lock and wait for the something to be placed on the queue.

    Oops, now we still have a problem. What if after we release the lock but before we wait for something to be placed on the queue, something is placed on the queue? In that case, we will be waiting for something that already happened.

    Condition variables exist to solve this exact problem. They have an atomic "unlock and wait" operation that closes this window.

    So await must hold the lock because otherwise there would be no way to ensure you weren't waiting for something that already happened. You must hold the lock to prevent another thread from racing with your wait.