The AQS J.U.C

AbStractQueuedSynchronizer classes, referred to as “AQS, is a framework to build the locks and synchronizer, JDK1.5 began to introduce J.U.C, greatly enhances the concurrency of JAVA program, while AQS is the core of J.U.C, is the core part of concurrent classes, he is a based on FIFO queue This queue can be used to build locks or other related synchronization infrastructure

AQSThe underlying structure

The bottom layer uses a bidirectional linked list, which is an implementation of the queue, so it can be treated as a queue. Sync queue is a bidirectional linked list consisting of heAN nodes (mainly used for subsequent scheduling) and tail nodes. Condition queues are not required. One-way lists exist only when conditions are needed, and there may be multiple Condition queues

  • FIFO queues are implemented using Node and can be used to build the basic framework for locks and other synchronization devices
  • An int is used to represent the state. In AQS, there is a state member variable. Based on AQS, there is a synchronization component ReentrantLock. In this component, state represents the number of threads acquiring locks. State > 1 indicates the number of locks
  • The method of use is inheritance. The AQS design is based on a template approach, using methods that need to inherit the AQS and override them.
  • Subclasses manipulate state by inheriting methods that manage their state {acquire() and release()} through methods that implement it
  • Both exclusive and shared lock modes (exclusive and shared) can be implemented. All of its subclasses implement and use either its proprietary API or its shared lock functionality, without using both sets of apis. Even its better-known subclass, ReentrantReadWirteLock, is implemented with two internal class read and write locks using two separate sets of apis. AQS has two functions: exclusive control and shared control.
  • Locks in the LOCK package (commonly used ReentrantLock, ReadWriteLock) are built based on AQS. However, these locks do not inherit AQS directly. Instead, a Sync class is defined to inherit AQS. Because locks are user-oriented and synchronizers are thread oriented, aggregating synchronizers in the lock implementation rather than directly inheriting AQS is a good way to separate the two concerns.

Based on the above design, the general idea of AQS concrete implementation

AQS maintains a CLH queue to manage locks. Threads will first try to acquire locks. If they fail, they will pack the current thread and wait status into nodes and add them to the Sync queue. If the current node is head, the next node will attempt to acquire the lock. If it fails, it will block itself until it is woken up. And when the thread that holds the lock releases the lock, it wakes up the subsequent thread in the queue. Based on these basic designs and ideas, the JDK provides a number of subclasses based on AQS.

Summary of exclusive locking process

After AQS template method acquire fails to obtain synchronization state by calling tryAcquire, a subclass custom implementation -> constructs the thread as a Node Node (creates an exclusive Node) (addWaiter)-> Add a Node Node to the end of the synchronization queue (addWaiter)-> The Node spins to get the synchronization state (acquirQueued). In the state of the spin for synchronization, only its precursor node is the first time will try to get synchronous state, if the node precursor is not head node or the precursor of the node is a single head node failed to get the synchronization status, the judgment of the current thread to block, if you need to block you will need to be awakened later returned. When releasing the synchronization state, the synchronizer calls the tryRelease(int arg) method to release the synchronization state and then wakes up the successor nodes of the head node.

Summary of shared locking process

The main difference between shared and exclusive acquisition is that multiple threads can simultaneously acquire the synchronous state. The synchronization state can be shared by calling the acquireShared(int arg) method.

The synchronizer calls the tryAcquireShared(int arg) method to try to obtain the synchronization status. The return value is int. If the return value is greater than 0, the synchronization status can be obtained. Therefore, in the shared fetch spin process, the synchronization state is successfully obtained and the spin exits only if the tryAcquireShared(int arg) method returns a value greater than or equal to 0. Shared release of synchronous state state is done by calling releaseShared(int arg)

CountDownLatch, ReentrantReadWriteLock, and Semaphore are shared to obtain synchronization status.

Follow wechat public account: [entry station] unlock more knowledge points