AQS explain the shared lock mode

General

 

AQS frame data structure is a two-way FIFO queue when multiple threads compete for resources, competition those threads that failed to be added to the queue. He provides a lot of interfaces to the upper layers, one of which is acquireShared acquire a shared mode interface. This paper will be based on a step by step analysis of this interface, access to resources is how the failure of the thread into the queue, the queue into how the queue is again compete resources, the following is a general flow acquireShared performed:

  1. TryAcquireShared multiple threads by calling the Get method of sharing resources, the return value is greater than or equal to 0 is acquired resources succeeds, the return value is less than 0 acquisition failure.

  2. After the failure of the current thread acquired shared resource by calling addWaiter method of this thread package Node node, and the node is set to the shared mode. The node is then added to the tail of the queue.

  3. After the addition to the end, is determined on a node of the node is not the head of the queue node, if the head node, then the node is a node-queue and acquire a shared resource, while calling setHeadAndPropagate method to the node set as a new head nodes, and all shared queue types of nodes wake up, go get a shared resource. If the acquisition fails, then added to the queue again.

  4. If the node is not the first node precursor nodes, then loop through for autorotation to wait until the current node precursor node is head node, end spin.

This is the shared mode AQS compete for resources failed general process, here let us have a general impression, is how to operate through the following detailed analysis of the source code.

 

AQS shared lock mode

 

AQS acquire a shared lock by calling acquireShared () method of this top-level, we look at the source code for this method:

public final void acquireShared(int arg) {
   if (tryAcquireShared(arg) < 0)
       doAcquireShared(arg);
}

This method has a determination if, when tryAcquireShared () return value is less than 0 failed to acquire the lock, enter doAcquireShared () method. tryAcquireShared method is used to get locked in shared mode, for tryAcquireShared () This method is the focus of our look at him return value. It is written in jdk1.8

* @return a negative value on failure; zero if acquisition in shared
*         mode succeeded but no subsequent shared-mode acquire can
*         succeed; and a positive value if acquisition in shared
*         mode succeeded and subsequent shared-mode acquires might
*         also succeed, in which case a subsequent waiting thread
*         must check availability. (Support for three different
*         return values enables this method to be used in contexts
*         where acquires only sometimes act exclusively.)  Upon
*         success, this object has been acquired.

When the failure of the return is negative, if it was acquired in shared mode 0 for success, but the next node sharing mode it can not succeed. If the return is a positive number that is greater than 0, which indicates the current thread acquires a shared success model, and behind it threads can also acquire a shared mode.

When the shared mode acquire failed, we look at the source code to do what doAcquireShared operation

private void doAcquireShared(int arg) {
   final Node node = addWaiter(Node.SHARED);
   boolean failed = true;
   try {
       boolean interrupted = false;
       for (;;) {
           final Node p = node.predecessor();
           if (p == head) {
               int r = tryAcquireShared(arg);
               if (r >= 0) {
                   setHeadAndPropagate(node, r);
                   p.next = null; // help GC
                   if (interrupted)
                       selfInterrupt();
                   failed = false;
                   return;
              }
          }
           if (shouldParkAfterFailedAcquire(p, node) &&
               parkAndCheckInterrupt())
               interrupted = true;
      }
  } finally {
       if (failed)
           cancelAcquire(node);
  }
}

 

First call addWaiter () method, which is mainly packaged as a Node node, and the node is added to the tail of the queue. Here incoming sharing model parameters, the node becomes a shared mode.

The current thread is added to the queue, then the node acquired by the spin precursor (for (;;)), if the predecessor node is a head node, then the call tryAcquireShared () method to obtain the status of the current node, that this method has the return value above introduced, equal to 0 means do not wake successor node, only greater than 0 will wake up all the nodes behind.

If successfully acquire shared resources, call setHeadAndPropagate method sets the current node is the first node, and let the original head of the queue node. If interrupted in the process of acquiring a spin lock, then the current thread is interrupted.

If the precursor node is not the head of the current node, by shouldParkAfterFailedAcquire determine the current state of the thread, if the thread is blocked returns true, otherwise it returns false. ParkAndCheckInterrupt method refers to the current thread in the process of acquiring the lock of whether an interrupt event, if the current thread state blocked and it is interrupted then put the flag is interrupted update to true.

If an exception occurs call cancelAcquire method, this method is to update the current node status is canceled, and clear the node.

setHeadAndPropagate we look at the source code for this method

private void setHeadAndPropagate(Node node, int propagate) {
   Node h = head; // Record old head for check below
   setHead(node);//设置当前节点为头节点
   if (propagate > 0 || h == null || h.waitStatus < 0 ||
      (h = head) == null || h.waitStatus < 0) {//符合状态的将全部唤醒
       Node s = node.next;
       if (s == null || s.isShared())
           doReleaseShared();
  }
}

This method of delivery of the two parameters, a current node, a return value is tryAcquireShared method. We see the source code from which first records the current head node, then it () method to obtain the current node is set to lock the head node setHead. If statements continue to meet the requirements of the wake-up successor nodes, if a node is empty then the next call doReleaseShared method, doReleaseShared method continues wake node behind. This method will be explained in detail in the shared lock is released.

 

Shared lock is released

ReleaseShared we look at the source code, this method is sharing mode top method to release resources.

public final boolean releaseShared(int arg) {
   if (tryReleaseShared(arg)) {//
       doReleaseShared();
       return true;
  }
   return false;
}

tryReleaseShared method to obtain the release of a shared resource model, if successfully released then calls doReleaseShared continue to wake up the next node.

We continue to look at the specific wake-up operation doReleaseShared () This method

private void doReleaseShared() {
   for (;;) {
       Node h = head;
       if (h != null && h != tail) {
           int ws = h.waitStatus;
           if (ws == Node.SIGNAL) {
               if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                   continue;            // loop to recheck cases
               unparkSuccessor(h);
          }
           else if (ws == 0 &&
                    !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
               continue;                // loop on failed CAS
      }
       if (h == head)                   // loop if head changed
           break;
  }
}

We have found through the source code, if the current thread state is Node.SIGNAL, Node.SIGNAL value is -1, is a static constant, this value indicates the current thread is suspended. If the current thread is suspended, then updates the current state of the thread is 0. If the update fails then continue. Call unparkSuccessor after a successful update () This method is the first node wake shared lock. If the head node itself belonging to a reset state waitStatus == 0, and it is set to a propagation state of node then propagating down.

Let's look at the source code of this method unparkSuccessor

private void unparkSuccessor(Node node) {

   int ws = node.waitStatus;
   if (ws < 0)
       compareAndSetWaitStatus(node, ws, 0);

   Node s = node.next;
   if (s == null || s.waitStatus > 0) {
       s = null;
       for (Node t = tail; t != null && t != node; t = t.prev)
           if (t.waitStatus <= 0)
               s = t;
  }
   if (s != null)
       LockSupport.unpark(s.thread);
}

From this method, we found that if the head of state of the thread is less than zero, then put the current thread is reset to 0. Why is it less than 0, the articles have said, waitStatus <0 waiting or suspended state. That is, if the current thread is waiting for a pending state, then the current thread state is reset to 0. Then find the next node if the next node is empty or under a thread has been canceled, then there is a case has not been removed from the head to find the node. When the next node is not empty when the wake-up call LockSupport.unpark method of the current thread. LockSupport.unpark calls Unsafe this class calls the native method execution.

Guess you like

Origin www.cnblogs.com/fengyun2050/p/12384608.html