Condition variable related code is hard to understand. So let me explain

There are a few strange things about Condition related code in Java concurrency. I’ll use the following code to show you.

The code is about one cup and two threads. One thread is trying to fill it, the other to drink from it.

The code uses explicit Lock + Condition API. But what I’m going to explain also applies to implicit lock + object monitor (i.e. synchronized and wait()/notify())

package concurrency.producer_consumer;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Cup {

    private volatile boolean filled = false;

    private Lock lock = new ReentrantLock();
    private Condition sharedCondition = lock.newCondition(); //"shared" because both drinker and filler are using it


    public void drink() throws InterruptedException {
        try {
            lock.lock(); //l1
            while (!filled) { //lp1
                sharedCondition.await();  //w1
            }
            System.out.println(Thread.currentThread().getName() + " just drank from the cup"); //a1
            filled = false;
            sharedCondition.signal(); //s1

        } finally {
            lock.unlock(); //ul1
        }
    }

    public void fill() throws InterruptedException {
        try {
            lock.lock();
            while (filled) {
                sharedCondition.await();
            }
            System.out.println(Thread.currentThread().getName() + " just filled the cup");
            filled = true;
            sharedCondition.signal();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Cup cup = new Cup();

        Thread filler = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    cup.fill();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }


        }, "filler");

        Thread drinker = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    cup.drink();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "drinker");


        drinker.start();
        filler.start();

        drinker.join();
        filler.join();
    }


}

There are a few things that are not intuitive:

  • The sharedCondition is called a condition, but it’s not a predicate. It’s just a dummy object which itself doesn’t carry any logic.
  • Instead, you need to use your own state variable to mean the real condition. Here it’s filled
  • You may think line l1 and ul1 together form a pair for locking/unlocking, and defines a critical section. But not really.
    • If there is no need to wait, then yes, they are a pair
    • Otherwise
      • Line l1 and some unlocking inside w1 form a pair. sharedCondition.await() releases the lock.
      • some locking inside line w1 and line ul1 form a pair. When a thread exits sharedCondition.await() , it will acquire the lock
  • Where does the program go after going into sharedCondition.await() ? Is the method calling over? And what happened after being woken up?
    • When a thread goes into line w1, the thread sleeps, but the method call of drink() is not over, as the call of sharedCondition.await() is not over
    • After being woken up, sharedCondition.await() then finishes. And then the thread moves up to line lp1, to check the state again. If now the state is satisfying, it moves down to line a1

One more thing to help understanding the code is that there is a queue of sleeping threads, related to each condition.

  • By condition.await() , current thread puts itself into a queue until some biz state is reached
  • By condition.signal(), current thread wakes up another thread in the queue

Hope you can now use Lock and Condition more smoothly.

Leave a Comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.