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:
- Object.wait() and Object.notify() (or Object.notifyAll()) – These methods are used when working with intrinsic locks (via the synchronized block).
- 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.