<img alt="" src="https://secure.leadforensics.com/150446.png " style="display:none;">

Limitations of intrinsic lock and solution

Madhura Oak Jun 04, 2012

ReentrantLock ReentrantReadWriteLock lock fairness Java ReadWriteLock Lock API intrinsic lock Multithreading monitor lock Technology

Java SE 5.0 introduced classes for locking called as Lock API. They are part of java.util.concurrent.locks package. Until then, only synchronized methods and synchronized blocks were available for locking.

A built-in lock called intrinstic lock or monitor lock is obtained on the object which is locked by synchronized method or block. This lock is acquired by the thread executing synchronized method/block and released when the thread leaves it. The intrinsic lock is released under the following scenarios:

  • the synchronized method/block execution is completed normally
  • there is an exception thrown which is not handled within the synchronized method/block
  • wait() is called in the synchronized method/block

Limitations of intrinsic lock

The intrinstic lock acts as a mutex (mutally exclusive lock) where only one thread can run in the object’s monitor until the lock is released. There cannot be more than one owning thread in an intrinsic lock.

The intrinsic locks are mutually exclusive locks. In an object, we may have a property which can be read or modified concurrently by multiple threads. The threads which are reading the property cannot obtain a shared lock.

When an intrinsic lock is acquired by a thread on an object the other threads trying to access the object’s monitor will have to keep waiting until the executing thread releases the lock and leaves the object’s monitor. When a thread leaves the object’s monitor, the JVM can grant its lock to any other thread waiting for its access and not necessarily the oldest thread in the waiting state. In other words, there is no fairness guarentee on lock acquisition when synchronization is used. We can also say that intrinsic locks are unfair. The JVM guarentees that all threads waiting for access to the object’s monitor will be granted access before its shutdown but it does not guarentee the order of locking. Thus, intrinsic locks could lead to thread starvation.

There is no non-blocking way to obtain a lock using synchronization. The thread which wants to obtain a lock will have to keep waiting if the lock is being held by other thread. The requesting thread cannot check whether a lock is available and perform other operations if its unavailable.

When a thread acquires an intrinsic lock, it cannot be interrupted or the thread’s task cannot be cancelled before the thread leaves the intrinsic lock.

There is no way to obtain the collection of threads waiting to acquire the lock. We also cannot determine the number of threads waiting to acquire the lock.

Lock API

To overcome the limitations of intrinsic lock you can use the Lock API. This API provides Lock, ReadWriteLock and Condition interfaces and their implementing classes.

The wait() allows only one thread to own the object’s monitor. If you need to use multiple wait-sets in a single object then you will have to use the Condition object provided by the Lock factory method newCondition(). Read this example of bounded buffer implementation using multiple conditions.

The ReadWriteLock implementing classes can be used by a Publisher-Subscriber framework where the publisher thread modifies a property and subscriber threads read it. Shared locks can be held by a subscriber threads instead of the mutually exclusive intrinsic lock for better concurrency. The ReentrantReadWriteLock is the implementing class for ReadWriteLock.

ReentrantLock is an implementation of Lock which provides reentrant mutually exclusive lock. If you want to grant locks to the threads in the same order they asked for it then you can create a fair lock. A fair lock can be created by using new ReentrantLock(true). However, use fair locks only when required as they reduce throughput in comparison with the unfair locks.

The Lock interface provides a tryLock() method with which a lock can be obtained in a non-blocking way. A thread can attempt to obtain a lock and it will be granted if it is not being held by any other thread. The thread requesting for a lock need not keep waiting for the lock. This is also called as polled lock. The lock obtained using tryLock() is unfair. You can use the alternate tryLock(0, TimeUnit.SECONDS) method to obtain a fair lock if the fairness is set to true using the ReentrantLock constructor. In case of unfair locking, the current thread requesting for a lock using tryLock() will acquire the lock even if other threads are waiting for the lock. In the case of fair locking, the current thread will not acquire the lock if other threads are waiting for it. This method returns true if the thread obtains the lock on object’s monitor.

There is also a timed tryLock method to specify the time limit for which a thread can wait to obtain the lock. This method considers the fairness setting.

The Lock interface provides lockInterruptibly() method with which the lock obtained by the executing thread will be released when the thread is interrupted. It allows a thread to stop execution and release the lock before completion of its task when interrupted.

The getQueuedThreads() and getQueueLength() methods of ReentrantLock can be used to get the collection of threads waiting to acquire the lock and to get the number of threads waiting to acquire the lock respectively.

Performance comparison of ReentrantLock and synchronized

The ReentrantLock provides same semantics as synchronized method/block. In Java 5 JVM, the ReentrantLock has better performance than synchronized under thread contention. However, in Java 6 there is no significant difference in the performance of ReentrantLock and synchronized.

When should we use the Lock API?

The major drawback of ReentrantLock, ReentrantReadWriteLock.ReadLock and ReentrantReadWriteLock.WriteLock classes is that the lock needs to be explicitly released within the finally block. A programmer may forget to release the lock when its use is over. The Lock API classes should be used only when you need to overcome the limitations of the built-in synchronized method/block. Only expert programmers should write the code which use these classes.

e-Zest is a leading digital innovation partner for enterprises and technology companies that utilizes emerging technologies for creating engaging customers experiences. Being a customer-focused and technology-driven company, it always helps clients in crafting holistic business value for their software development efforts. It offers software development and consulting services for cloud computing, enterprise mobility, big data and analytics, user experience and digital commerce.