Condition Variables

In multithreading, condition variables allow threads to communicate about a particular condition or state that must be met before further processing. Condition variables are used in conjunction with synchronization constructs like synchronized blocks or Lock objects. They allow threads to wait for a certain condition to become true and notify other threads when that condition changes.

In Java, condition variables can be implemented using:

  1. Object.wait() and Object.notify() (or Object.notifyAll()) – These methods are used when working with intrinsic locks (via the synchronized block).
  2. Condition interface from java.util.concurrent.locks package – This offers a more flexible and explicit way of managing thread synchronization with conditions when using Lock objects.

Condition Interface in Java

The Condition interface, found in the java.util.concurrent.locks package, provides a means for one thread to suspend execution (wait) until notified by another thread that some condition may now be true. It works with Lock (especially ReentrantLock) to offer explicit thread communication.

Common Methods in Condition Interface

Method Description
void await() Causes the current thread to wait until it is signaled or interrupted.
boolean await(long time, TimeUnit unit) Waits for a specific time or until signaled.
long awaitNanos(long nanosTimeout) Waits for a time (in nanoseconds) or until signaled.
void signal() Wakes up one waiting thread.
void signalAll() Wakes up all waiting threads.
void awaitUninterruptibly() Waits uninterruptibly until signaled.

Program Implementation

//ConditionVariableWithLockDemo.java
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.ArrayList;
import java.util.List;

class SharedResourceWithLock {
    private List<Integer> list = new ArrayList<>();
    private final int CAPACITY = 5;
    private Lock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();

    public void produce() throws InterruptedException {
        int value = 0;
        while (true) {
            lock.lock();
            try {
                // Wait if the list is full
                while (list.size() == CAPACITY) {
                    notFull.await();
                }
                System.out.println("Produced: " + value);
                list.add(value++);
                notEmpty.signal();  // Signal the consumer
            } finally {
                lock.unlock();
            }
            Thread.sleep(1000);
        }
    }

    public void consume() throws InterruptedException {
        while (true) {
            lock.lock();
            try {
                // Wait if the list is empty
                while (list.isEmpty()) {
                    notEmpty.await();
                }
                int value = list.remove(0);
                System.out.println("Consumed: " + value);
                notFull.signal();  // Signal the producer
            } finally {
                lock.unlock();
            }
            Thread.sleep(1000);
        }
    }
}

public class ConditionVariableWithLockDemo {
    public static void main(String[] args) {
        SharedResourceWithLock resource = new SharedResourceWithLock();

        Thread producerThread = new Thread(() -> {
            try {
                resource.produce();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        Thread consumerThread = new Thread(() -> {
            try {
                resource.consume();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        producerThread.start();
        consumerThread.start();
    }
}

/*C:\>javac ConditionVariableWithLockDemo.java

C:\>java ConditionVariableWithLockDemo
Produced: 0
Consumed: 0
Produced: 1
Consumed: 1
Produced: 2
Consumed: 2
Produced: 3
Consumed: 3
Produced: 4
Consumed: 4
Produced: 5
Consumed: 5
Produced: 6
Consumed: 6
Produced: 7
Consumed: 7
Produced: 8
Consumed: 8
Produced: 9
Consumed: 9
Produced: 10
Consumed: 10
*/

The Condition interface is a powerful tool in Java’s concurrency toolkit, offering fine-grained control over thread communication when used with Lock (typically ReentrantLock). Unlike traditional wait()/notify(), Condition allows multiple independent condition variables, enabling more structured and readable code in complex synchronization scenarios like bounded buffers, custom blocking queues, or advanced producer-consumer patterns. Its methods like await(), signal(), and signalAll() enhance precision and flexibility, making Condition essential for scalable, thread-safe, and maintainable concurrent applications.

Scroll to Top