JDK growth 21: what are ReentrantLock fair, unfair and reentrant locks?

Posted by ErikTheViking on Tue, 04 Jan 2022 22:01:58 +0100

After the previous three sections, I believe you have a clear understanding of the AQS principle at the bottom of ReentrantLock. Next, let's introduce some concepts in ReentrantLock:

  • The concept of fair and unfair lock
  • How does ReentrantLock achieve unfairness and fairness?
  • What is a reentrant lock?

Fair lock Vs non fair lock

Fair lock Vs non fair lock

When you master the principle of ReentrantLock locking, locking failure, joining the team and releasing the lock. In fact, several concepts need to be understood in ReenrantLock, such as exclusive lock, shared lock, reentrant lock, fair lock and unfair lock.

In this section, let's talk about fair and unfair locks.

What is a fair lock? What is an unfair lock? Here is an example:

I believe you must have had the experience of queuing. For example, you lined up for your girlfriend to buy milk tea. But you lined up well. Suddenly, when a boss, relative or related household came and joined a line, how did you feel? Does it feel unfair. However, some related households are also very cultured. They will not jump in the queue and will honestly queue up. This is very fair, because they come first and arrive first.

This is actually the meaning of fair and unfair lock. You can think about the above example. Thread 2 is queuing. At this time, thread 1 releases the lock, but suddenly a thread 3 comes to add the lock. Is it possible that thread 3 grabs the lock when thread 2 leaves the queue? This is unfair. Thread 3 cuts in the queue and does not queue honestly.

However, if thread 3 honestly queues up and enters the AQS queue, it is a fair lock. As shown in the figure below:

How does ReentrantLock achieve unfairness and fairness?

How does ReentrantLock achieve unfairness and fairness?

How does the specific code do it? The core is through two subclasses of Sync. FairSync and NonfairSync. From the name, you should know that these two classes mean fair and unfair AQS Sync components.

You can find the differences between the two classes:

static final class NonfairSync extends Sync {
 private static final long serialVersionUID = 7316153563782823691L;


 final void lock() {
​    if (compareAndSetState(0, 1))
​      setExclusiveOwnerThread(Thread.currentThread());
​    else
​      acquire(1);
 }


 protected final boolean tryAcquire(int acquires) {
​    return nonfairTryAcquire(acquires);
 }
}


final boolean nonfairTryAcquire(int acquires) {
 final Thread current = Thread.currentThread();
 int c = getState();
 if (c == 0) {
​    if (compareAndSetState(0, acquires)) {
​      setExclusiveOwnerThread(current);
​      return true;
​    }
 }

 else if (current == getExclusiveOwnerThread()) {
​    int nextc = c + acquires;
​    if (nextc < 0) // overflow
​      throw new Error("Maximum lock count exceeded");
​    setState(nextc);
​    return true;
 }
 return false;

}
static final class FairSync extends Sync {
  private static final long serialVersionUID = -3000897897090466540L;

  final void lock() {
​    acquire(1);
  }

 

  protected final boolean tryAcquire(int acquires) {
​    final Thread current = Thread.currentThread();
​    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 first is the lock method. The difference is that in an if judgment, the unfair lock NonfairSync will have one more judgment. First try to add a lock.

What does this difference mean? You can understand that if thread 1 is released and someone else comes to lock it, you can directly try to plug in the queue first. It is possible that thread 2 in the AQS queue has not been awakened, but someone has robbed the lock and let other threads lock successfully.

How to release the lock? Another is how to let the thread at the head of the queue wake up and try to obtain the lock after the lock is completely released.

The other way is to try locking. The only difference is an if condition

hasQueuedPredecessors()

This method can be seen from the name to judge whether there are elements in the next queue. The code is as follows:

 public final boolean hasQueuedPredecessors() {
  Node t = tail; // Read fields in reverse initialization order
  Node h = head;
  Node s;
  return h != t &&
   ((s = h.next) == null || s.thread != Thread.currentThread());
}

In other words, in the code that attempts to lock a fair lock, there is a restriction that if someone queues, other threads cannot jump in the queue and lock. Therefore, even if thread 1 releases the lock and thread 3 comes to add the lock, because the lock method does not have the if of the unfair lock (come up to try CAS to modify the state and lock the code), thread 3 can only join the queue. If thread 3 executes the code trying to obtain the lock, the fair lock has one more code than the code of the unfair lock to judge whether there is a waiting thread in the queue. If so, you can only line up. As shown in the figure below:

Reentrant lock Vs non reentrant lock

Reentrant lock Vs non reentrant lock

As mentioned earlier, ReentrantLock involves some concepts of locks. After talking about the concepts of fair and unfair locks, today we finally talk about reentrant locks.

In fact, this is easy to understand. ReentrantLock cleverly realizes reentrant locking through the state variable of AQS. If the same thread calls the lock method and locks, state will add + 1 to the existing value. Each time the lock is added again, it is a reentrant, so it is a reentrant lock. in other words:

The same thread can use the same ReentrantLock to lock repeatedly.

In addition, if a lock is released, it must be released many times. If the same thread locks several times, it needs to be released several times. It is necessary to restore the state value to 0 to truly release the lock, and other threads can obtain it.

Because it is relatively simple, we won't take you to see the source code implementation. You can find it in the source code yourself. The core is to master the principle of AQS locking and releasing.

Exclusive lock VS shared lock

Exclusive lock VS shared lock

As for the concepts of exclusive and shared locks, they will be mentioned later when explaining read-write locks. Let's talk about it briefly.

The so-called exclusive lock means that as long as one thread is locked, others have to stand aside. This lock belongs to a thread exclusive, which is an exclusive lock.

Default reentrantlock What is the lock created by lock? Unfair reentrant exclusive lock!

What does shared lock mean? It means that it can hold a lock with other threads at the same time, such as the read-write lock to be locked later. Thread 1 has a read lock, but thread 2 can still add a read lock. They share a lock. Such a lock is a shared lock.

Of course, there are some mutually exclusive relationships between read-write locks, so in the next section, we will explore how to use read-write locks, the specific principle of read-write locks, and the mutually exclusive relationship of read-write locks.

Summary & Thinking

Summary & Thinking

In fact, this section is not particularly responsible for complex knowledge. It mainly shows you the reentrant implementation of reentrant lock

The core idea is to judge whether the queue has a waiting thread through code execution sequence, CAS operation sequence and an if.

Secondly, it introduces several concepts, such as reentrant lock, fair lock, unfair lock, exclusive lock and shared lock.

In fact, the principle of ReentrantLock has been roughly analyzed here. You have at least mastered the operation design of ReentrantLock based on encapsulating three components (variables) based on abstract classes.

Did we actually use this idea to design the synchronized underlying ObjectMonitor we mentioned earlier, but one is the ObjectMonitor object encapsulated in C + + and the other is the ReentrantLock object encapsulated in Java.

After learning the principle and source code of a technology or a project, we must learn to think. After thinking, we can better apply this technology and solve problems.

In addition, interested students can go deep into how to implement the CAS underlying JVM C + + language and why to use LockSupport Park suspends the thread, the park method implementation of LockSupport, and so on..

In the next section, we begin to study the implementation principle of ReentrantReadWriteLock. See you in the next section!

This paper is based on the operation tool platform of blog group sending one article and multiple sending OpenWrite release

Topics: JDK