In Java, each object provides an implicit monitor lock also called as intrinsic lock, which is used by its synchronized methods to ensure that only one thread can execute a critical section of code at a time. The critical section of code should allow only one thread to execute it at a time to avoid race conditions and is defined by synchronized methods. However, one disadvantage of intrinsic lock is that it offers only one mutex.
Java monitor pattern is useful when you want to implement more than one mutexes in your code. Instead of using the intrinsic lock, lock is obtained on other Java objects other than one in which the critical section of code is defined. This pattern also decouples the locks and the code which needs synchronized access.
The Java monitor pattern implementation which defines two mutexes is given in Listing 1. There are two groups of methods which should be executed mutually exclusive to each other – methodA and methodB form one group and methodC and methodD the other – in such a way that while executing critical section A, critical section B should not get executed but critical sections C and D can be executed by a concurrently executing thread.
public class ThreadSafeClass { private Object lock1 = new Object(); private Object lock2 = new Object(); public void methodA() { synchronized(lock1) { //critical section A } } public void methodB() { synchronized(lock1) { //critical section B } } public void methodC() { synchronized(lock2) { //critical section C } } public void methodD() { synchronized(lock2) { //critical section D } } }
Listing 1. Java monitor pattern implementation with two mutexes
Instead of using an Object instance as a lock and using synchronized blocks, the Lock API can also be used. An advantage of using Lock API is that the code can try to obtain a lock using tryLock
or wait for a certain timeout to obtain lock by using tryLock
with timeout methods – instead of forcefully obtaining a lock. The Lock API also provides the use of Condition which allows a lock to be relinquished by suspending an executing thread and allows other threads to execute while the thread is waiting. However, care should be taken to explicitly release the lock in finally block while using Lock API.
The Java Monitor pattern thus allows fine-grained locking. This pattern is used by legacy classes such as Vector and Hashtable.
The clients should not be allowed to access the locks by keeping them private. If the locks are private, the client also cannot use the synchronization directly. This will avoid the liveness problems that can be caused due to improper use of locks by the clients.