Analyze the implementation principle of AQS through reentrantlock

For Java developers, they will encounter multi-threaded access to public resources. At this time, they often lock to ensure the correctness of the access results. In Java, the following two methods are usually used to solve the problem of locking:

Synchronized is supported at the bottom of Java, while concurrent package is implemented by JDK. You can read about the principle of synchronized. If someone asks you what synchronized is, send him this article.

Lock interface

Lock is an interface, and the method is defined as follows

Implementation of lock

There are many classes that implement the lock interface. The following are some common lock implementations

AQS (AbstractQueuedSynchronizer)

The full name of AQS is (abstractqueuedsynchronizer). This class is also under java.util.concurrent.locks. It is an abstract class designed using the template mode in the design mode. It internally provides a series of public methods, which are mainly used through inheritance. It does not implement any synchronization interface, but only defines the methods for obtaining and releasing synchronization status Method to provide a custom synchronization component.

It can be said that as long as you understand AQS, most APIs in j.u.c can be easily mastered.

The following figure shows the subclass of AQS:

As you can see, AQS still has many subclasses. AQS will be explained in detail below.

AQS principle overview

AQS solves the security problem of multi-threaded access to shared resources. Its schematic diagram can be shown as follows:

AQS uses a volatile int variable state to represent the synchronization status. When other threads access shared resources with locks, they will be blocked and then put into the CLH of FIFO (Craig, landin, and Hagersten)

Queue, waiting to be awakened here. When the thread acquiring the lock releases the lock, it will wake up a blocked node (thread) from the queue. This ensures that each thread accesses the shared resources in order to avoid data inconsistency.

AQS frame diagram

Let's take a look at the AQS framework as a whole through an architecture diagram:

CLH queue

As mentioned earlier, AQS uses the built-in FIFO queue to complete the queuing work of obtaining resource threads. Since it is a queue, it is composed of many nodes. Let's look at the data composition of nodes.

For the node class, it can be found that its internal operations are guaranteed to be atomic operations through the unsafe class. At the same time, some internal variables are decorated with volatile to ensure that the variable is also visible to other threads. In addition, it can be concluded that there are two different modes, one is exclusive mode and the other is shared mode.

Take another look at the two attributes related to node class in AQS:

The whole structure is shown in the figure below:

Queue operation

As shown in the figure above, we understand the structure of the synchronization queue. It is simple to analyze its listing operation. Nothing more than pointing the tail (using CAS to ensure atomic operation) to the new node, the prev of the new node to the last node in the queue (the old tail node), and the next node of the last node in the original queue to the new node, so as to establish a connection and draw a diagram to help you understand.

Out of line operation

The synchronization queue (CLH) follows FIFO. The first node is the node that obtains the synchronization status. After the thread of the first node releases the synchronization status, it will wake up its successor node (next), and the subsequent node will set itself as the first node when the synchronization status is obtained successfully. This process is very simple. As shown in the following figure

Setting the head node is completed by the thread that successfully obtains the synchronization status (obtaining the synchronization status is completed by CAS). Only one thread can obtain the synchronization status. Therefore, CAS is not required to ensure the operation of setting the head node. Only set the head node as the successor node of its original head node and disconnect the next node of the original head node (waiting for GC recycle) application.

Synchronization state

After understanding the data structure, let's take a look at the synchronization state of AQS - state. A field named state is maintained in AQS, which means synchronization status. It is modified by volatile and is used to display the locking status of current critical resources.

Here are several ways to access this field:

These methods are all final modified, indicating that they cannot be overridden in subclasses. We can modify the synchronization state represented by the state field to realize the exclusive mode and shared mode (locking process) of multithreading.

@H_ 419_ 461@

For our customized synchronization tool, we need to customize the way to obtain synchronization status and release status, that is, the first layer in the AQS architecture diagram: API layer.

It should be noted that the meaning of state is different for different AQS implementations.

After knowing the basic architecture of AQS, let's analyze the implementation principle of AQS, still taking reentrantlock as the model.

Principle analysis of reentrantlock implementation

Features Overview

Reentrant lock means reentrant lock, which means that a thread can repeatedly lock a critical resource. In order to help you better understand the features of reentrantlock, we first compare reentrantlock with the commonly used synchronized. Its features are as follows (the blue part is the main analysis point of this article):

The following is a more intuitive comparison through pseudo code:

Sequence diagram of reentrantlock

Call the lock () method in reentrantlock. The calling process of the source code is shown in the sequence diagram:

It can be seen from the figure that when the lock acquisition fails, the addwaiter () method will be called to encapsulate the current thread as a node and add it to the AQS queue. Based on this idea, let's analyze the source code implementation of AQS. Relationship between reentrantlock and AQS

First, let's take a look at the construction method of reentrantlock. There are two construction methods, as follows:

It can be found that the constructor refers to two internal classes, fairsync (fair lock) and nonfairsync (non fair lock). And are subclasses of sync.

The importance of the sync class can also be found here, and the screenshot above also shows that sync is a subclass of abstractqueuedsynchronizer. Here, the relationship between them has surfaced:

For fairsync and nonfairsync:

The differences in the implementation of fair locks and unfair locks will be explained later in the article. The following analysis still takes unfair locks as the main analysis logic.

Lock method

For reentrantlock, the default is nonfairsync. Let's take this as an example to understand the principle behind it.

See the meaning of lock method code:

The code implementation logic of compareandsetstate is as follows

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>