Saturday 22 February 2014

Mutex in Java : Explained using Producer Consumer Problem

A mutex is a kernel resource that provides synchronization services. Intrinsic locks or monitors in Java act as mutexes, which means that only one thread can own the lock at one time. When thread A attempts to acquire a lock held by the thread B, thread A has to wait till thread B releases the lock.

 Every Java object can intrinsically act as a lock for purposes of synchronization. These build in locks are called intrinsic locks or monitor locks. The lock is automatically acquired by the executing thread before entering the synchronized block or method and is automatically released when the control exits the synchronized block, by the normal control flow or by throwing an exception out of the synchronized block. One important point to note is that Java locks are reentrant. When a thread attempts to acquire a lock held by another thread, it has to wait, but if a thread attempts to acquire a lock held by itself, it succeeds.

As noted above, that each object in Java can act as a lock, we need to careful while guarding a shared resource using a lock. For each shared resource, that may be accessed by multiple threads, all accesses to that resource must be performed with the same lock held. There is no relation between an object's state and it's intrinsic lock. The state of an object need not be guarded by the intrinsic lock.

Let's move to Producer Consumer problem to demonstrate the use of intrinsic locks in Java. Since intrinsic locks serve as mutexes, this example demonstrates the use of mutexes as well.

Problem:
Consider two threads : Producer thread and Consumer thread. The producer's job is to generate a piece of data and put it into a buffer shared with the consumer. The consumer's job is to consume the data from the buffer . The problem is to make sure that the producer won't try to add into the buffer if it's full and that the consumer won't try to remove data from an empty buffer.

For demonstration purpose, we have used just a count variable instead of a buffer. The producer would produce only if the count is 0 and increment count to 1. Consumer would only consume if the count is 1 and set count to 0.

Here's the code:


Here are few important points that we must note :
  1. Note that wait() and notify() are called from a synchronized block. This is because of race condition. We may lose a notify signal if these methods are not called from a synchronized block.
  2. wait() and notify() methods are defined in the Object class. This is because every Java object has a monitor associated with it.
  3. Why are we waiting in a loop in both produce() and consume() methods ? This is because of spurious wakeups. It is possible for threads to wake up even if notify() or notifyAll() has not been called. This while loop is also a nice solution if you have multipe threads waiting, which are all awakened using notifyAll(), but only one of them lets the lock. The others should continue waiting.
  4. wait() method releases the acquired lock/monitor. Otherwise the above code could easily go into a deadlock.

No comments:

Post a Comment