In-depth understanding of AQS exclusive lock ReentrantLock source code analysis

Monitor—the design philosophy of Java synchronization

  • Monitor: refers to the management of shared variables and the operation process of shared variables, so that they can support concurrency.
  • Mutual exclusion: Only one thread is allowed to access shared resources at the same time;
  • Synchronization: How threads communicate and collaborate.

MESA model

In the history of tube process development, three different tube process models have appeared successively, namely Hasen model, Hoare model and MESA model. What is now widely used is the MESA model . The concept of condition variables
Insert image description here
is introduced in the monitor , and each condition variable corresponds to a waiting queue. The function of the condition variable waiting queue is to solve the synchronization problem between threads.

There are two implementations of monitors in Java

  • One is the Object-based Monitor mechanism, which is used to implement synchronized built-in locks.
  • One is the abstract queue synchronizer AQS, which is used to implement the Lock mechanism under the JUC package.

AQS principle analysis

What is AQS

  Most synchronizer implementations in the java.util.concurrent package revolve around common basic behaviors, such as waiting queues, conditional queues, exclusive acquisitions, shared acquisitions, etc., and the abstraction of these behaviors is based on AbstractQueuedSynchronizer (AQS for short ) . , AQS is an abstract synchronization framework that can be used to implement a state-dependent synchronizer.

Most of the synchronizers provided in the JDK, such as Lock, Latch, Barrier, etc., are implemented based on the AQS framework:

  • Generally, AQS is inherited through an internal class Sync.
  • Map all synchronizer calls to the corresponding method of Sync

Features of AQS:

  • blocking wait queue
  • shared/exclusive
  • fair/unfair
  • reentrant
  • Allow interrupts

AQS core structure

AQS internal maintenance attribute volatile int state
state represents the available status of the resource

State has three access methods :

  • getState()
  • setState()
  • compareAndSetState()

Two resource access methods are defined:

  • Exclusive-exclusive, only one thread can execute, such as ReentrantLock
  • Share - sharing, multiple threads can execute at the same time, such as Semaphore/CountDownLatch

AQS mainly implements the following methods:

  • isHeldExclusively(): Whether the thread is occupying resources exclusively. Only when condition is used do you need to implement it.
  • tryAcquire(int): exclusive mode. Try to obtain the resource, returning true if successful and false if failed.
  • tryRelease(int): exclusive mode. Try to release the resource, returning true if successful and false if failed.
  • tryAcquireShared(int): sharing method. Try to get resources. A negative number indicates failure; 0 indicates success, but there are no remaining available resources; a positive number indicates success, and there are remaining resources.
  • tryReleaseShared(int): Sharing mode. Try to release the resource. If the subsequent waiting node is allowed to be awakened after the release, it returns true, otherwise it returns false.

AQS defines two queues

  • Synchronous waiting queue: Mainly used to maintain threads that join the queue when acquiring locks fail.
  • Conditional waiting queue: When await() is called, the lock will be released, and then the thread will join the conditional queue. When signal() is called to wake up, the thread node in the conditional queue will be moved to the synchronization queue, waiting to obtain the lock again.

AQS defines 5 node statuses in the queue:

  • The value is 0, initialization state, indicating that the current node is in the sync queue, waiting to acquire the lock.
  • CANCELLED, the value is 1, indicating that the current thread is canceled;
  • SIGNAL, the value is -1, indicating that the threads contained in the successor nodes of the current node need to be run, that is, unpark;
  • CONDITION, the value is -2, indicating that the current node is waiting for condition, that is, in the condition queue;
  • PROPAGATE, the value is -3, indicating that subsequent acquireShared can be executed in the current scenario;
    Insert image description here

Synchronous waiting queue

The synchronous waiting queue in AQS is also called the CLH queue. The CLH queue is a queue based on a doubly linked list data structure invented by Craig, Landin, and Hagersten. It is a FIFO first-in-first-out thread waiting queue. The CLH queue in Java is the original CLH A variant of the queue, the thread changes from the original spinning mechanism to a blocking mechanism.

AQS relies on CLH synchronization queue to complete the management of synchronization status:

  • If the current thread fails to obtain the synchronization status, AQS will construct the current thread's waiting status and other information into a node (Node) and add it to the CLH synchronization queue, while blocking the current thread.
  • When the synchronization state is released, the first node will be awakened (fair lock) so that it can try to obtain the synchronization state again.
  • Transfer nodes in the condition queue to the synchronization queue through signal or signalAll. (Converted from conditional queue to synchronous queue)
    Insert image description here

Conditional waiting queue

The condition queue in AQS is saved using a one-way list and is connected using nextWaiter:

  • Call the await method to block the thread;
  • The current thread exists at the head node of the synchronization queue and calls the await method to block (convert from the synchronization queue to the conditional queue)

ReentrantLock source code analysis

ReentrantLock is an application implementation based on the AQS framework . It is a synchronization method for concurrent thread access in the JDK. Its function is similar to synchronized , which is a mutex lock that can ensure thread safety .
Basic usage of ReentrantLock

public class ReentrantLockTest {
    
    
    private final ReentrantLock lock = new ReentrantLock();
    // ...

    public void doSomething() {
    
    
        lock.lock();  // block until condition holds
        try {
    
    
            // ... method body
        } finally {
    
    
            lock.unlock();
        }
    }
}

Issues to pay attention to when reading source code

  • Fair and unfair locks, how reentrant locks are implemented
  • The essence of design: How to design the enqueue and dequeue operations in concurrent scenarios
    • Implementation of blocking logic when threads compete for locks and fail to join the queue
    • Logic implementation of lock-releasing threads waking up blocked threads and dequeuing to compete for locks
      Insert image description here

Guess you like

Origin blog.csdn.net/beautybug1126/article/details/132003899