Home » Java

Implicit & Explicit Locks

23 November 2008 10,105 views No Comment

Explicit locks

The problem with implicit locks is that it is possible to lock out parts of a class which do not necessarily have to be locked out from each other.

Since Java 5, another way to ensure exclusive access to a critical section is by the use of exclusive locks. We will re-write the producer-consumer solution using exclusive locks to demonstrate them. Explicit locks are implementations of Lock interface. A concrete lock implementation provided in Java is ReentrantLock. Each lock class has a pair of lock() and unlock() methods. The following code demonstrates how to use them.

public void m() {
	Lock l = new ReentrantLock();
	l.lock();
	try {
	} finally {
		l.unlock();
	}
}

Unlike implicit locks, these locks are not automatically released when a method exits. Hence, you should always wrap them in a try/finally block. You can create a condition on which an explicit lock waits. The method Lock.newCondition creates a Condition. A thread can wait for a condition to become true by invoking condition.await(). Other threads can signal that a certain condition has been met by invoking condition.signal(). For the producer-consumer implementation we will create two conditions – notFull and notEmpty. Producer threads will be made to wait until notFull condition has been met and likewise, consumer threads will be made to wait until notEmpty condition has been met. An advantage of ReentrantLock is that it provide methods to visualize concurrency. For example ReentrantLock.getQueuedThreads() returs all threads waiting for a given lock. The solution can be re-written using exclusive locks as shown below:

/**
 * @(#)ExclusiveLocksDemo.java	Apr 11, 2008
 */
package lock;
 
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * Demonstration of the use of <code>Lock</code> classes.
 * 
 * @author $Author: vijaykandy $
 * @version $Revision: 1.4 $
 */
public class ExplicitLocksDemo {
	/**
	 * Main.
	 * 
	 * @param s
	 */
	public static void main(String s[]) {
		Drop drop = new Drop();
		Consumer c1 = new Consumer("C1", drop);
		Producer p1 = new Producer("P1", drop);
 
		(new Thread(p1)).start();
		(new Thread(c1)).start();
	}
 
	/**
	 * Shared class
	 * 
	 * @author $Author: vijaykandy $
	 * @version $Revision: 1.4 $
	 */
	static class Drop {
		private String shared;
 
		private boolean empty = true;
 
		private final ReentrantLock lock = new ReentrantLock();
 
		private final Condition notFull = lock.newCondition();
 
		private final Condition notEmpty = lock.newCondition();
 
		/**
		 * Default constructor.
		 */
		public Drop() {
		}
 
		/**
		 * Takes the message after aquiring a lock.
		 * 
		 * @return
		 */
		public String get() {
			try {
				lock.lock();
				printLockStatistics();
 
				while (empty) {
					notEmpty.awaitUninterruptibly();
				}
 
				String message = shared;
				empty = true;
				notFull.signal();
				return message;
			} finally {
				lock.unlock();
			}
		}
 
		/**
		 * Sets a message after aquring a lock.
		 * 
		 * @param s
		 */
		public void set(String s) {
			try {
				lock.lock();
				printLockStatistics();
 
				while (!empty) {
					notFull.awaitUninterruptibly();
				}
 
				shared = s;
				empty = false;
				notEmpty.signal();
			} finally {
				lock.unlock();
			}
		}
 
		private void printLockStatistics() {
			System.err.println("    No. of threads waiting:" + lock.getQueueLength());
			System.err.println("    No. of holds:" + lock.getHoldCount());
		}
	}
 
	/**
	 * Produces a message
	 * 
	 * @author $Author: vijaykandy $
	 * @version $Revision: 1.4 $
	 */
	static class Producer implements Runnable {
 
		private String name;
 
		private Drop drop;
 
		private List<String> messages = new ArrayList<String>() {
			{
				add("Let's use guarded");
				add("blocks to create a");
				add("Producer-Consumer");
				add("application. This");
				add("kind of application");
				add("shares data between two");
				add("threads: the producer,");
				add("that creates the data,");
				add("and the consumer,");
				add("that does something ");
				add("with it.");
				add("DONE");
 
			}
		};
 
		public Producer(String name, Drop drop) {
			this.name = name;
			this.drop = drop;
		}
 
		public void run() {
			Random random = new Random();
			for (String msg : messages) {
				drop.set(msg);
				System.out.format("%s produced: %s%n", name, msg);
				try {
					Thread.sleep(random.nextInt(5000));
				} catch (InterruptedException e) {
				}
			}
		}
	}
 
	/**
	 * Consumes a message
	 * 
	 * @author $Author: vijaykandy $
	 * @version $Revision: 1.4 $
	 */
	static class Consumer implements Runnable {
 
		private String name;
 
		private Drop drop;
 
		public Consumer(String name, Drop drop) {
			this.name = name;
			this.drop = drop;
		}
 
		public void run() {
			Random random = new Random();
			while (true) {
				String message = drop.get();
				System.out.format("%s received: %s%n", name, message);
 
				if (message.equals("DONE")) {
					return;
				}
 
				try {
					Thread.sleep(random.nextInt(5000));
				} catch (InterruptedException e) {
				}
			}
		}
	}
}

Pages: 1 2 3 4

Leave your response!

Add your comment below, or trackback from your own site. You can also subscribe to these comments via RSS.

Be nice. Keep it clean. Stay on topic. No spam.

You can use these tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">

This is a Gravatar-enabled weblog. To get your own globally-recognized-avatar, please register at Gravatar.