AQS Series (a) - ReentrantLock of lock

Foreword

    AQS i.e. AbstractQueuedSynchronizer, JUC abstract class is a core package, JUC package directly or indirectly most operations are achieved through it. This is the first AQS series, followed by continuously updated articles, for the commonly used functions JUC package AQS-related clear, on the one hand to consolidate their knowledge, on the one hand and you can also learn from each other Friends of the Park. Cold winter, use technology to warm themselves.

First, the relationship between the AQS and ReentrantLock

    First class ugly offer a homemade map

 

 

 

     From the bottom up, there are two internal classes ReentrantLock FairSync internal static type and NonfairSync, represent the fair and unfair lock latch (note lock is achieved ReentrantLock reentrant exclusive lock). Both static inner classes and the common heritage of a static internal ReentrantLock abstract class Sync, this abstract class inheritance AQS.

    Clear the relationship classes, we look together at the source below.

Second, the interpretation of the source

    Non-equity lock ReentrantLock default constructor is created, it can be specified to generate a fair lock by passing true. Here we have a fair process of locking the lock, for example, to interpret the source code . Before reading the source code need to be clear about the AQS state property, which is of type int, state = 0 indicates that the current lock is not occupied, state = 1 representation is occupied , and if re-entry state, the re-entry state is several times a few.

 1 public class JucLockDemo1 {
 2     public static void main(String[] args){
 3         ReentrantLock lock = new ReentrantLock(true);
 4         Thread t1 = new Thread(() -> {
 5             lock.lock();
 6             // 业务逻辑
 7             lock.unlock();
 8         });
 9         t1.start();
10         System.out.println("main end");
11     }
12 }

    The method wherein the line 5 lock points into the code:

1 public void lock() {
2         sync.lock();
3     }

    Direct transfer of sync method of lock, the lock sync the following method is an abstract method, logical method depends on the specific implementation class, because we have created here is fair locks, so look at it lock into FairSync ways:

1 final void lock() {
2             acquire(1);
3         }

    lock method FairSync in very simple, direct call acquire method, parameter is 1, continue to follow:

1 public final void acquire(int arg) {
2         if (!tryAcquire(arg) &&
3             acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
4             selfInterrupt();
5     }

    AQS located acquire method, it is very important, although only a short period of three lines, but there is a lot of content. Next, there are methods to interpret.

Method 1: tryAcquire (arg)

    This method is implemented in FairSync in the code as follows:

. 1  protected  Final  Boolean to tryAcquire ( int Acquires) {
 2              Final the Thread Current = Thread.currentThread ();
 . 3              int C = getState ();
 . 4              // Analyzing state state, if it is 0 indicates the lock is free, can try to acquire 
. 5              IF ( == 0 C ) {
 . 6                  IF (hasQueuedPredecessors (!) &&
 . 7                      compareAndSetState (0 , Acquires)) {
 . 8                      setExclusiveOwnerThread (Current);
 . 9                      return  to true ;
 10                  }
 . 11             } // exclusiceOwnerThread stored exclusively is currently running thread, where it is determined if true, the second note is the current thread locking, can re-entry, but want +. 1 State 
12 is              the else  IF (Current == getExclusiveOwnerThread () ) {
 13 is                  int NEXTC = C + Acquires;
 14                  IF (NEXTC <0 )
 15                      the throw  new new Error ( "COUNT Lock the Maximum exceeded Number" );
 16                  the setState (NEXTC);
 . 17                  return  to true ;
 18 is              }
 . 19              return  to false ;
 20 is          }

    If the second determination is well understood, and the counterweight is ReentrantLock exclusive support (So it is reentrant exclusive lock), but after the determination logic 0 c == more trouble.

    First, understand how the current logical: if state = 0 Description lock idle, but also because it is fair locks, it must first determine whether the current queue AQS there are no queued task, if not, a CAS will go into a state, then set exclusive thread of execution to obtain the right to perform; if there are tasks in the queue, then the method must first acquire a false return. It can be inferred, hasQueuedPredecessors method is used to determine whether there is queued in the queue .

    Lea point go in and see the power of God to realize the logic of it.

 1 public final boolean hasQueuedPredecessors() {
 2         // The correctness of this depends on head being initialized
 3         // before tail and on head.next being accurate if the current
 4         // thread is first in queue.
 5         Node t = tail; // Read fields in reverse initialization order
 6         Node h = head;
 7         Node s;
 8         return h != t &&
 9             ((s = h.next) == null || s.thread != Thread.currentThread());
10     }

    Code is small, but relatively obscure meaning of the expression. The first judge h! = T, if h = t, explained the queue is empty, then this judgment condition is false, the direct method returns, and this time out if the inverse is true, CAS will continue to take the state to seize and exclusive thread acquires the lock road this situation left over. If h! = T is true, there will now be described in the task queue, then enters the rear braces ((s = h.next) == null || s.thread! = Thread.currentThread ()), have the task in the queue case, there are two possibilities, one is in the queue first task is the current thread, the other is the first task is not the current thread. Because it is fair to perform in front of the lock, if the first task of the current thread, then it has the right to apply to go about acquiring the lock, if the first task is not the current thread, then the current thread obediently line up it, so finished to wheel your turn. Back braces is a distinction between these two cases, we use reverse logic to analyze. Methods hasQueuedPredecessors said that if the current thread can lock to compete returns false, can not compete lock braces result is returned back true is false, then the current thread will go to seize the lock, or a calculation of how to be false? Or both sides are false, that is to (s = h.next)! = Null && s.thread == Thread.currentThread (), meaning that the first task in the queue is not empty and the first task is the current thread, rather than those in the above-described source || && this is logically equivalent, so here meaning becomes clear, the two conditions && return connection means: determining whether the queue is not empty and (p a task is empty or not the current thread).

    hasQueuedPredecessors method finished, tryAcquire method are not any difficulties, and then we return to acquire (int arg) method starts above. If tryAcquire returns true, explain to acquire the lock, then the process would not go back; if it was false, then enter acquireQueue. But we look at the inside of addWaiter method.

方法2:  addWaiter(Node.EXCLUSIVE), arg)

     This method for generating a node of the current node thread and put it in the tail, the method of source code:

. 1  Private the Node addWaiter (the Node MODE) {
 2          the Node node = new new the Node (Thread.currentThread (), MODE); // Create a node for the current thread node
 . 3          // the Try The path of FAST ENQ; ENQ Full Backup to ON failure 
. 4          Pred = the node tail;
 . 5          IF (! Pred = null if) {// Analyzing the tail is empty, if not empty node then the node in the back stitching
 . 6              node.prev = Pred; // node connecting the node to the tail node
 . 7              IF (compareAndSetTail (Pred, node)) {// the CAS node by node into the tail
 . 8                  pred.next = node; // if the CAS operation is successful, then the next original queue tail node to the node node composition deque
 . 9                  return Node;
10              }
 . 11          }
 12 is          ENQ (Node); // here it can be two cases: 1, the tail is empty; 2, the tail is not empty, but when the CAS operation due to preempt other threads cause failure;
 13 is          return Node;
 14      }

We should be able to sort through the notes clear logic, focusing on the following talk about realization enq (node) method:

. 1  Private the Node ENQ ( Final the Node Node) {
 2          for (;;) {
 . 3              the Node T = tail;
 . 4              IF (T == null ) { // Must the initialize the tail is null, in line with said first front case 
5                  IF (compareAndSetHead ( new new the Node ())) // set the head of the queue
 . 6                      tail = head; // the tail head of the queue are initialized to empty Node
 . 7              } the else {// the tail is not empty, in front of said second case, this processing logic is defined above for the case of processing pred = null!
 . 8                  node.prev = T;
 . 9                  IF (compareAndSetTail (T, Node)) {
10                     t.next = node;
11                     return t;
12                 }
13             }
14         }
15     }

    This method can be seen that an infinite loop, until executing the logic else. One thing to note here is that if the beginning of the queue is empty, that tail is null, will trigger the end of the initialization of the first team squad, after initialization once again will enter circulation else, the node into the back end of the original team return t. Note that the return of t is not used, it is in the methods of the other scene.

 METHOD 3: acquireQueued (final Node node, int arg)

     This method is used to acquire the lock, the return value indicates the current thread lock to get in the process of acquiring the lock of whether the interruption, the following look at the source code:

. 1  Final  Boolean acquireQueued ( Final the Node Node, int Arg) {
 2          Boolean failed = to true ;
 . 3          the try {
 . 4              Boolean interrupted = to false ;
 . 5              for (;;) {
 . 6                  Final the Node P = node.predecessor (); // get the current a node before the node
 . 7                  IF (p == head && to tryAcquire (Arg)) {// if p == head node is described first task, then it can acquire the lock go through to tryAcquire
 . 8                      setHead (node); // acquiring the lock is successful, it will put the team first location node, and thread and prev is set to null
 9                      p.next =null ; // Next help GC then p is set to null, all cut off contact with the outside world 
10                      failed = to false ;
 . 11                      return interrupted;
 12 is                  } // the following two methods is important if that focused on the
 13 is                  if (shouldParkAfterFailedAcquire (P, Node) &&
 14                      parkAndCheckInterrupt ())
 15                      interrupted = to true ;
 16              }
 . 17          } the finally {
 18 is              IF (failed)
 . 19                  cancelAcquire (Node);
 20 is          }
 21 is     }

    By notes, I believe that if the first logic can understand clearly, we focus on the next two methods if the second explanation.

    The first is shouldParkAfterFailedAcquire (p, node) method, this method is logic:

. 1  Private  static  Boolean shouldParkAfterFailedAcquire (Pred Node, Node node) {
 2          int WS = pred.waitStatus; //. 1, for the new node Node, this state are (addConditionWaiter new node only if the node is not 0) 0
 . 3          IF ( == ws Node.SIGNAL)
 . 4              //. 3, the device 2 will ws after -1, this method returns false, then the outer loop for walk around, there will enter the second time when this method, direct return true. -1 represents the state of the current thread may park
 . 5              return  to true ;
 . 6          IF (WS> 0 ) {
 . 7  
. 8              do {
 . 9                  node.prev = Pred = pred.prev;
 10              } the while (pred.waitStatus> 0);
 . 11              pred.next = Node;
 12 is          } the else {
 13 is              // 2, is ws = 0, then there will enter the set state -1,0 ws indicates not Park
 14              compareAndSetWaitStatus (Pred, ws, the Node. the SIGNAL);
 15          }
 16          return  to false ;
 . 17      }

    If the return is true, then entered the second method will suspend the current thread:

1 private final boolean parkAndCheckInterrupt() {
2         LockSupport.park(this);
3         return Thread.interrupted();
4     }

    The current thread of execution surface finish, wake up this thread and they will begin the cycle continues logic for acquiring the lock from the third row until acquiring the lock.

 

    Here, ReentrantLock the lock method will be the end, the whole process is. JUC package to see the source code, you can see written in a very simple, sometimes one or two simple conditions to judge it represents a lot of meaning, it shows that programmers careful and ease of strength, so read the source code, there is a look this grid reasoning pleasure on the novel-like thinking.

    The next section we'll unlock method of principle, and the last one in this section will be able to connect the next issue will again!

 

 

 

Guess you like

Origin www.cnblogs.com/zzq6032010/p/12002803.html