Table of contents
Open Table of contents
Introduction
Locks are a mechanism to prevent access of a resource to multiple threads at the same time.
Locks vs synchronized keyword
private static ReentrantLock lock = new ReentrantLock();
private static void accessResource() {
lock.lock();
try {
// do work
// access the resource
} finally {
lock.unlock();
}
}
This is very similar to synchronized keyword
private static void accessResource() {
synchronized (this) { // same as lock.lock();
// do work
// access the resource
} // same as lock.unlock();
}
Locks are explicit i.e. we have to do most of the things manually while synchronized is implicit as JVM manages the locking logic behind the scenes. We do not have to tell the code exactly when to grab the key or when to give it back.
Synchronized keyword can be used to make a method thread safe as well as make a block of code thread safe. Same can be achieved with reentrant locks too.
| Feature | synchronized (Implicit) | Lock (Explicit) |
|---|---|---|
| Ease of Use | Very easy | Tricky if not used carefully |
| Release | Automatic (at end of block). | Manual (must use finally block). |
| Flexibility | Rigid; block-structured only. | High; can span across methods. |
| Interruption | Thread will wait forever. | Can be interrupted or time out. |
Reentrant locks
Why “Reentrant”? Because we can call lock() method multiple times which is not possible with
synchronized.
private static ReentrantLock lock = new ReentrantLock();
private static void accessResource() {
lock.lock();
lock.lock();
// do work
// access the resource
lock.unlock();
lock.unlock();
}
private static ReentrantLock lock = new ReentrantLock();
private static void accessResource() {
lock.lock();
// update the shared resource
if (someCondtion()) {
accessResource(); // same thread entering the same lock again, increasing the `getHeldCount()`
}
lock.unlock();
}
Reentrant lock has a constructor with boolean parameter that decides if it will be fair lock. By default locks are unfair. By fair it means that it allows the thread waiting the longest in wait queue to acquire lock first. Unfair lock will allow threads that are making call to acquire lock while there are other threads waiting in wait queue, unfair but fast.
| Advantage | Disadvantage | |
|---|---|---|
| Fair lock | Equal chance for all threads | Slower |
| Unfair lock | Faster (more throughput) | Possible thread starvation (threads may wait forever) |
Reentrant lock has a utility method tryLock()
private static ReentrantLock lock = new ReentrantLock();
private static void accessResource() {
boolean lockAquired = lock.tryLock(5, SECONDS);
if (lockAcquired) {
try {
// do work
// access the resource
} finally {
lock.unlock();
}
} else {
// do something else when lock is not available
}
}