Lock API in Java

Overview

Lock API in Java is a more flexible and easy way for achieving the synchronization mechanism over the traditional use of synchronized keyword.

Lock API was introduced in java 1.5 release. It is defined inside the java.util.concurrent.lock package.

This tool for controlling access to a shared resource by multiple threads. In multithreaded programs, access to shared variables must be synchronizedΒ to prevent race conditions.a lock provides exclusive access to a shared resource: only one thread at a time can acquire the lock and all access to theΒ  shared resource requires that the lock be acquired first. However, some locks may allow concurrent access to a shared resource, such as the read lock of a ReadWriteLock.

Difference Between Lock and synchronized block

A synchronized block does not provide fairness to the multiple threads because any thread can acquire the object lock once previous existing lock is released
We can achieve fairness within the Lock APIs by specifying the fairness property. It makes sure that longest waiting thread is given access to the lock.

A thread gets blocked if it can’t get an access to the synchronized block. The Lock API provides tryLock() method.
The thread acquires lock only if it’s available and not held by any other thread. This reduces blocking time of thread waiting for the lock.

A thread which is in β€œwaiting” state to acquire the access to synchronized block, can’t be interrupted. The Lock API provides a method lockInterruptibly()
which can be used to interrupt the thread when it’s waiting for the lock

we can have Java Lock API’s lock() and unlock() operation in separate methods

Object monitor lock acquired through synchronized block is released automatically once its execution of block or method is completed. But in case of Lock api in java, lock needs to be released explicitly.

Lock API

Let us have a look at the methods of the Lock interface:

  1. void lock() – acquire the lock if it’s available; if the lock isn’t available a thread gets blocked until the lock is released.
  2. void lockInterruptibly() – this is similar to the lock(), but it allows the blocked thread to be interrupted and resume the execution through a thrown java.lang.InterruptedException
  3. boolean tryLock() – this is a non-blocking version of lock() method; it attempts to acquire the lock immediately, return true if locking succeeds
  4. boolean tryLock(long timeout, TimeUnit timeUnit) – this is similar to tryLock(), except it waits up the given timeout before giving up trying to acquire the Lock
  5. void unlock() – unlocks the Lock instance
  6. A locked instance should always be unlocked to avoid deadlock condition.

Sample Code block which uses Lock API in Java:

Lock lock = …; lock.lock();

try {

// access to the shared resource

}

finally {

lock.unlock();

}

In addition to the Lock API in java , we have a ReadWriteLock interface which maintains a pair of locks, one for read-only operations,
and one for the write operation. The read lock may be simultaneously held by multiple threads as long as there is no write.

ReadWriteLock declares methods to acquire read or write locks:

Lock readLock() – returns the lock and used for reading
Lock writeLock() – returns the lock and used for writing

1. ReentrantLock

ReentrantLockΒ class implements theΒ LockΒ interface. It offers the same concurrency and memory semantics as the implicit monitor lock used usingΒ synchronizedΒ methods and statements, with more capabilities.

public class SharedClass {

ReentrantLock lock = new ReentrantLock();
int counter = 0;

public void doLock() {
lock.lock();
try {
// Important code here
count++;
} finally {
lock.unlock();
}
}
}

Note: wrap theΒ lock() and theΒ unlock()Β calls in theΒ try-finallyΒ block to avoid the deadlock situations.

Let’s see how theΒ tryLock()Β works:

TheΒ tryLock()Β method tries to acquire the lock without pausing the thread. That is, If the thread couldn’t acquire the lock because it was held by some other thread, then It returns immediately instead of waiting for the lock to be released.

You can also specify a timeout in theΒ tryLock()Β method to wait for the lock to be available –

lock.tryLock(1, TimeUnit.SECONDS);
public void doTryLock(){

boolean isLockAcquired = lock.tryLock(1, TimeUnit.SECONDS);

if(isLockAcquired) {
try {

} finally {
lock.unlock();
}
}
}
2. ReadWriteLock

ReadWriteLock consists of a pair of locks – one for read access and one for write access. The read lock may be held by multiple threads simultaneously as long as the write lock is not held by any thread.

ReentrantReadWriteLockΒ class implements theΒ ReadWriteLockΒ interface.

Let’s see rules for acquiring theΒ ReadLockΒ orΒ WriteLockΒ by a thread:

  • Read Lock – if no thread acquired the write lock or requested for it then multiple threads can acquire the read lock
  • Write Lock – if no threads are reading or writing then only one thread can acquire the write lock

Let’s see how to make use of theΒ ReadWriteLock:

public class HashMapWithReadWriteLock {

Map<String,String> sHashMap = new HashMap<>();
ReadWriteLock lock = new ReentrantReadWriteLock();
Lock writeLock = lock.writeLock();

public void put(String key, String value) {
try {
writeLock.lock();
sHashMap.put(key, value);
} finally {
writeLock.unlock();
}
}

public String remove(String key){
try {
writeLock.lock();
return sHashMap.remove(key);
} finally {
writeLock.unlock();
}
}
}

I hope you understood well about Lock API. Thanks…

Newsletter Updates

Enter your name and email address below to subscribe to our newsletter

2 Comments

  1. This is truly useful, thanks.

Leave a Reply