SoFunction
Updated on 2025-04-06

Teach you a complete understanding of ReentrantLock reentry lock

1. Introduction to ReentrantLock

ReentrantLock reentry lock is a class that implements the Lock interface and is also a lock that is used frequently in actual programming. It supports reentry and means that it can repeatedly lock shared resources, that is, the current thread acquires the lock and acquires it again and will not be blocked. In the Java keyword synchronized implicitly supports reentry (you can read this article about synchronized), synchronized achieves reentry by obtaining self-increasing and releasing self-decreasing. At the same time, ReentrantLock also supports two methods: fair lock and unfair lock.

So, if you want to fully understand ReentrantLock, it is mainly about learning the synchronous semantics of ReentrantLock: 1. The implementation principle of reentrant; 2. Fair lock and non-fair lock.

2. The principle of realizing reentry

To support reentry, two problems need to be solved:

1. When a thread acquires a lock, if the thread that has already acquired the lock is the current thread, it will be successfully obtained directly;

2. Since the lock will be acquired n times, the lock will be completely released only after the lock is released n times.

Through this article, we know that the synchronization component mainly expresses its own synchronization semantics by rewriting several protected methods of AQS.

To address the first question, let’s take ReentrantLock to see how it is implemented. Taking the unfair lock as an example, judging whether the current thread can obtain the lock as an example. The core method is nonfairTryAcquire:

final boolean nonfairTryAcquire(int acquires) {
final Thread current = ();
int c = getState();
//1. If the lock is not occupied by any thread, the lock can be obtained by the current threadif (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//2. If occupied, check whether the occupied thread is the current threadelse if (current == getExclusiveOwnerThread()) {
// 3. Get it again, add one countint nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}

The logic of this code is also very simple, please refer to the comments for details.

In order to support reentry, processing logic is added in the second step. If the lock has been occupied by the thread, it will continue to check whether the occupied thread is the current thread. If so, adding 1 to the synchronization state returns true, indicating that it can be obtained successfully again.

Each time you re-acquire, you will add one to the synchronization state. So what is the processing idea when releasing? (It's still a case of unfair locking) The core method is tryRelease:

protected final boolean tryRelease(int releases) {
//1. Synchronous state decreases by 1int c = getState() - releases;
if (() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
//2. Only when the synchronization state is 0, the lock is successfully released and returns truefree = true;
setExclusiveOwnerThread(null);
}
// 3. The lock is not fully released, returns falsesetState(c);
return free;
}

Please refer to the comments for the logic of the code. It should be noted that the reentry lock must be released until the synchronization state is 0 before the lock is successfully released, otherwise the lock will not be released. If the lock is acquired n times and n-1 times is released, the lock is not fully released and returns false. Only when it is released n times can it be considered to be successfully released, and return true.

Until now, we can clarify the implementation of ReentrantLock's reentry, that is, we have understood the first item of synchronous semantics.

3. Fair lock and fair lock

ReentrantLock supports two types of locks: fair lock and unfair lock.

What is fairness? It is about acquiring a lock. If a lock is fair, the order of acquisition of the lock should conform to the absolute chronological order on the request and satisfy the FIFO. When the ReentrantLock construction method has no parameters, it is to construct an unfair lock, and the source code is:

public ReentrantLock() {
sync = new NonfairSync();
}

Another way is also provided, which can pass in a boolean value. When true, it is a fair lock, and when false, it is a non-fair lock. The source code is:

public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

When obtaining the non-fair lock (nonfairTryAcquire method) above, it simply obtains the current state and does some logical processing, and does not take into account the waiting situation of threads in the current synchronization queue.

Let's take a look at the processing logic of fair locks. The core method is:

protected final boolean tryAcquire(int acquires) {
final Thread current = ();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}

The logic of this code is basically the same as nonfairTryAcquire. The only difference is that the logical judgment of hasQueuedPredecessors is added. The method name can be used to determine whether the current node has a predecessor node in the synchronization queue. If there is a predecessor node, it means that there is a thread that requests resources earlier than the current thread, according to fairness, the current thread fails to request resources. If the current node does not have a predecessor node, then there is a need to make the subsequent logical judgment.

A fair lock is each time to acquire the lock from the first node in the synchronization queue, while a non-fair lock is not necessarily the case. It is possible that the thread that just released the lock can acquire the lock again.

Fair Lock VS Non-Fair Lock

Each time a fair lock acquires the lock, it is the first node in the synchronization queue, ensuring the absolute order of the time of requesting resources. Instead, the non-fair lock may cause the thread that has just released the lock to continue to acquire the lock next time, which may cause other threads to never be able to acquire the lock, causing "hunger".

In order to ensure absolute order in time, fair locks require frequent context switching, rather than fair locks, which will reduce certain context switching and reduce performance overhead. Therefore, ReentrantLock chooses a non-fair lock by default, in order to reduce some context switching and ensure greater system throughput.

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.