After the previous three sections, you are already familiar with the UNDERLYING AQS principles of ReentrantLock. Here are a few concepts from ReentrantLock:

  • The concept of fair, unfair locks
  • How is ReentrantLock unfair and fair?
  • What is a reentrant lock?

Fair locks Vs unfair locks

Fair locks Vs unfair locks

ReentrantLock ReentrantLock ReentrantLock ReentrantLock ReentrantLock ReentrantLock ReentrantLock There are several concepts that need to be understood in ReenrantLock, such as exclusive lock, shared lock, reentrant lock, fair lock and unfair lock.

In this section, we will talk about fair and unfair locks.

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

I believe you must have queuing experience, such as you queued for your girlfriend to buy milk tea. But you’ve been standing in line for a long time, and suddenly some boss relative or relative cuts in. How does it feel? Doesn’t feel like it’s fair. But some relations are also very cultured, will not jump the queue, will be honest to line up, this is very fair, because first come, first come.

This is essentially what fair and unfair locks mean. If thread 2 is waiting in line and thread 1 releases the lock, thread 3 suddenly comes in and locks the lock, then thread 3 May grab the lock while thread 2 is waiting in line. This is not fair. Thread 3 cuts the queue and does not queue properly.

But if thread 3 is just queueing up and entering the AQS queue, that’s a fair lock. As shown below:

How is ReentrantLock unfair and fair?

How is ReentrantLock unfair and fair?

How does the code do that? The core is through two subclasses of Sync. FairSync and NonfairSync. As you should know from the name, these two classes stand for Sync components of fair and unfair AQS.

You can find the difference between the two classes:

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


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


 protected final boolean tryAcquire(int acquires) {
​    returnnonfairTryAcquire(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) // overflowthrow new Error("Maximum lock count exceeded");
​    setState(nextc);
​    return true;
 }
 return false;

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

  final void lock(a) {
​    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; }}Copy the code

The first is the lock method, the difference is that in an if judgment, an unfair lock NonfairSync will have an extra judgment, first try to add a lock.

What does this difference mean? If thread 1 is released and someone else comes to join the queue, try to join the queue first. It is possible that thread 2 in the AQS queue has not been awakened yet, and someone else has grabbed the lock and let another thread lock successfully.

How to release the lock, and how to let the thread at the head of the queue wake up to try to acquire the lock if the lock is completely released.

The other way, try locking, the only difference is an if condition

hasQueuedPredecessors()
Copy the code

This method is known by its name, so determine if there are any elements in the queue. The code is as follows:

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

That is, in the code for the fair lock attempt, there is a restriction that if someone is queuing, no other thread can jump the queue and lock. So even if thread 1 releases the lock, thread 3 lock, because the lock method without a fair lock the if (up to try the CAS change state, locking code), thread can only team 3, if the thread 3 performs to try to obtain the lock code, the fair is more than a fair lock code lock a judgment, whether in the queue waiting thread. If you do, you have to wait in line. As shown below:

Reentrant locks Vs non-reentrant locks

Reentrant locks Vs non-reentrant locks

As mentioned earlier, ReentrantLock involves some concepts of locking, and after talking about the concepts of fair and unfair locking, we’ll finally talk about reentrantlocks today.

ReentrantLock uses the state variable of AQS to implement reentrant locking. If the same thread calls the lock method, state will add +1 to the existing value. Each time the lock is added, it will be reentrant. In other words:

The same thread can be locked repeatedly using the same ReentrantLock.

In addition, the lock must be released several times. If the same thread has locked several times, the lock must be released several times. The state value must be restored to 0 before the lock is truly released and other threads can acquire it.

Because it is relatively simple, I will not take you to see the source code. You can find it yourself in the source code. The core or master the principle of AQS lock release lock is the most important.

Exclusive locks VS shared locks

Exclusive locks VS shared locks

The concepts of exclusive and shared locks will be covered later when we talk about read-write locks. So let me give you a quick overview.

** The so-called exclusive lock, ** as long as one thread locks, others have to step aside, this lock belongs to a thread exclusive, this is an exclusive lock.

What is the lock created by default reentrantLock.lock? Unfair reentrant exclusive lock!

What does shared lock mean? This means that a lock can be held at the same time as another thread, such as a read/write lock to be held later. Thread 1 has a read lock, thread 2 can still have a read lock, they share a lock. Such a lock is a shared lock.

Of course, read-write locks are mutually exclusive, so in the next section we’ll explore how to use read-write locks, how they work, and how read-write locks are mutually exclusive.

Summary & thinking

Summary & thinking

There’s nothing particularly complicated about this section, but the ReentrantLock ReentrantLock implementation, right

The core idea is to determine whether the queue has a waiting thread implementation by code execution order, CAS operation order and an if.

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

ReentrantLock is designed to encapsulate three components (variables) in an abstract class.

Previously we mentioned synchronized’s underlying ObjectMonitor, in fact, is also used to design this idea, but one is C++ encapsulated ObjectMonitor object, one is Java encapsulated ReentrantLock object.

When you finish learning the principle of a technology and or source code, or after finishing a project, we must learn to think, thinking in order to better application of this technology, better problem solving.

In addition, interested students can go to explore how it uses CAS underlying JVM C++ language implementation, why LockSupport. Park thread suspension, LockSupport park method implementation and so on.

In the next section, we’ll take a look at how ReentrantReadWriteLock works, and we’ll see you in the next section!

This article is published by OpenWrite!