Home » Core Java » util » concurrent » locks » Condition » java.util.concurrent.locks.Condition Example

About Sotirios-Efstathios Maneas

Sotirios-Efstathios Maneas
Sotirios-Efstathios (Stathis) Maneas is a PhD student at the Department of Computer Science at the University of Toronto. His main interests include distributed systems, storage systems, file systems, and operating systems.

java.util.concurrent.locks.Condition Example

In this tutorial we will discuss about the Condition interface in Java. A Condition object, also known as condition variable, provides a thread with the ability to suspend its execution, until the condition is true. A Condition object is necessarily bound to a Lock and can be obtained using the newCondition() method.

Furthermore, a Condition enables the effect of having multiple wait-sets per object, by combining these sets with the use of a Lock implementation. Moreover, due to the fact that Conditions access portions of state shared among different threads, the usage of a Lock is mandatory. It is important to mention that a Condition must atomically release the associated Lock and suspend the current’s thread execution.

Finally, the Condition interface exists since the 1.5 version of Java.

The structure of the Condition interface

Methods

  • await()
  • The current thread suspends its execution until it is signalled or interrupted.

  • await(long time, TimeUnit unit)
  • The current thread suspends its execution until it is signalled, interrupted, or the specified amount of time elapses.

  • awaitNanos(long nanosTimeout)
  • The current thread suspends its execution until it is signalled, interrupted, or the specified amount of time elapses.

  • awaitUninterruptibly()
  • The current thread suspends its execution until it is signalled (cannot be interrupted).

  • await(long time, TimeUnit unit)
  • The current thread suspends its execution until it is signalled, interrupted, or the specified deadline elapses.

  • signal()
  • This method wakes a thread waiting on this condition.

  • signalAll()
  • This method wakes all threads waiting on this condition.

Conditions in Java

As we have already described, Conditions are being used in order for a thread to be notified, when a condition is true. One fundamental example that demonstrates the usage of Conditions is the producer-consumer example. According to this model, a thread produces a number of items and places them to a shared queue, while a thread consumes these objects, by removing them from the shared queue.

Important: Notice that the model supports the presence of multiple producers and consumers, but in this example, we will demonstrate the simple case where we have one producer and one consumer.

In addition, it is important to mention that the shared queue is accessed by multiple threads and thus, it must be properly synchronized. Our implementation of the shared queue is shown below:

SharedQueue.java:

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

public class SharedFiFoQueue {

	private Object[] elems = null;
	private int current = 0;
	private int placeIndex = 0;
	private int removeIndex = 0;
	
	private final Lock lock = new ReentrantLock();
	private final Condition isEmpty = lock.newCondition();
	private final Condition isFull = lock.newCondition();
	
	public SharedFiFoQueue(int capacity) {
		this.elems = new Object[capacity];
	}
	
	public void add(Object elem) throws InterruptedException {
		lock.lock();
		while(current >= elems.length)
			isFull.await();
	
		elems[placeIndex] = elem;
		
		//We need the modulo, in order to avoid going out of bounds.
		placeIndex = (placeIndex + 1) % elems.length;
		
		++current;
		
		//Notify the consumer that there is data available.
		isEmpty.signal();
		
		lock.unlock();
	}

	public Object remove() throws InterruptedException {
		Object elem = null;
		
		lock.lock();
		while(current <= 0)
			isEmpty.await();
	
		elem = elems[removeIndex];

		//We need the modulo, in order to avoid going out of bounds.
		removeIndex = (removeIndex + 1) % elems.length;
		
		--current;
		
		//Notify the producer that there is space available.
		isFull.signal();
		
		lock.unlock();
		
		return elem;
	}
}

The SharedQueue class contains a private array of elements and a maximum capacity. It supports two methods, add and remove, which are used to add and remove an element to the queue respectively. In both methods, the lock is first acquired. Then, if the queue is not full, an element can be inserted, or correspondingly, if the queue is not empty, an element can be removed. Finally, before the lock is released, both methods notify any waiting thread.

Producer.java:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class Producer extends Thread {
	private final static String FILENAME = "input.txt";
	private final SharedFiFoQueue queue;
	
	public Producer(SharedFiFoQueue queue) {
		this.queue = queue;
	}
	
	@Override
	public void run() {
		BufferedReader rd = null;
		
		try {
			rd = new BufferedReader(new FileReader(FILENAME));
			
			String inputLine = null;
			while((inputLine = rd.readLine()) != null) {
				String[] inputWords = inputLine.split(" ");
				
				for(String inputWord: inputWords)
					queue.add(inputWord);
			}
			
			//Terminate the execution.
			queue.add(null);
		}
		catch (InterruptedException ex) {
			System.err.println("An InterruptedException was caught: " + ex.getMessage());
			ex.printStackTrace();
		}
		catch (IOException ex) {
			System.err.println("An IOException was caught: " + ex.getMessage());
			ex.printStackTrace();
		}
		finally {
			try {
				if(rd != null)
					rd.close();
			}
			catch (IOException ex) {
				System.err.println("An IOException was caught: " + ex.getMessage());
				ex.printStackTrace();
			}
		}
	}
}

The Producer class reads the contents of the specified file, line-by-line. Each line is split into separated words and each word is placed into the shared queue. Once the file has been completely read, a special null object is placed into the queue, in order to notify the consumer that no more elements will be placed into the queue.

Consumer.java:

import java.util.HashSet;
import java.util.Set;

public class Consumer extends Thread {
	private final Set seenObjects = new HashSet();
	private int total = 0;
	private final SharedFiFoQueue queue;

	public Consumer(SharedFiFoQueue queue) {
		this.queue = queue;
	}
	
	@Override
	public void run() {
		try {
			do {
				Object obj = queue.remove();
				if(obj == null)
					break;
				
				if(!seenObjects.contains(obj)) {
					++total;
					seenObjects.add(obj);
				}
				
				System.out.println("[Consumer] Read the element: " + obj.toString());
				
			} while(true);
		}
		catch (InterruptedException ex) {
			System.err.println("An InterruptedException was caught: " + ex.getMessage());
			ex.printStackTrace();
		}
		
		System.out.println("\n[Consumer] " + total + " distinct words have been read...");
	}
}

The Consumer class constantly reads elements from the shared queue, until a special null object is received. The Consumer class also counts the number of distinct words, as received by the producer.

ConditionExample.java:

public class ConditionExample {
	public static void main(String[] args) throws InterruptedException {
		SharedFiFoQueue sharedQueue = new SharedFiFoQueue(10);
		
		//Create a producer and a consumer.
		Thread producer = new Producer(sharedQueue);
		Thread consumer = new Consumer(sharedQueue);
		
		//Start both threads.
		producer.start();
		consumer.start();
		
		//Wait for both threads to terminate.
		producer.join();
		consumer.join();
	}
}

In a sample main method, we create one instance of each class and wait for both threads to terminate. A sample execution is shown below:

[Consumer] Read the element: Lorem
[Consumer] Read the element: ipsum
[Consumer] Read the element: dolor
[Consumer] Read the element: sit
...
[Consumer] Read the element: in
[Consumer] Read the element: est.

[Consumer] 80 distinct words have been read...

Download the Eclipse Project

This was a tutorial about the Condition interface in Java.

Download
You can download the full source code of this example here: ConditionExamples.zip.
(No Ratings Yet)
2 Comments Views Tweet it!

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you our best selling eBooks for FREE!

 

1. JPA Mini Book

2. JVM Troubleshooting Guide

3. JUnit Tutorial for Unit Testing

4. Java Annotations Tutorial

5. Java Interview Questions

6. Spring Interview Questions

7. Android UI Design

 

and many more ....

 

Receive Java & Developer job alerts in your Area

 

2
Leave a Reply

avatar
2 Comment threads
0 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
2 Comment authors
Konstantin PavlovMadhavi Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
Madhavi
Guest
Madhavi

Producer and Consumer constructors were showing error because of the ‘queue’ instance variable declared as ‘final’.
private final SharedFiFoQueue queue;

Konstantin Pavlov
Guest