[JDK] JDK source code analysis -AbstractQueuedSynchronizer (3)

Outline

 

The foregoing " JDK source code analysis -AbstractQueuedSynchronizer (2) " analyzes the process AQS access to resources in exclusive mode, related operations in shared mode analysis paper.

 

In fact, the two most similar operation, understanding the previous analysis of exclusive mode, and then analyze the shared mode is relatively easy.

 

Sharing mode

 

Method Overview

 

Similarly the exclusive mode, there is a corresponding shared mode operation similar thereto, are as follows:

 

1. acquireShared (int arg): access to resources in shared mode, ignoring interrupts;

 

2. acquireSharedInterruptibly (int arg): access to resources in shared mode, interrupt response;

 

3. tryAcquireSharedNanos (int arg, long nanosTimeout): Gets resources in shared mode, interrupt response, and there is a timeout to wait;

 

4. releaseShared (int arg): release of resources, Wake successor node, and ensure the dissemination.

 

Their exclusive mode operation is relatively similar, the following specific analysis.

 

Analysis Methods

 

1. shared mode access to resources (ignoring interrupts)

 

acquireShared:

public  Final  void acquireShared ( int Arg) {
     // the return value is less than 0, represents the acquisition failure 
    IF (tryAcquireShared (Arg) <0 ) 
        doAcquireShared (Arg); 
} 

// attempt to obtain resources in shared mode (return value of type int) 
protected  int tryAcquireShared ( int Arg) {
     the throw  new new an UnsupportedOperationException (); 
}

Method tryAcquire exclusive mode and the like, in the AQS tryAcquireShared method also throws an exception, logic implemented by subclasses.

 

The difference is that, to tryAcquire method returns a boolean result, retrieves success; whereas tryAcquireShared returns a result of type int, respectively:

1) Negative: Indicates acquisition failure;

2) 0: succeed, but the subsequent acquisition fails sharing mode;

3) positive: that to succeed, get follow-sharing model may succeed (for testing required).

 

If tryAcquireShared acquisition is successful, the direct return; otherwise perform doAcquireShared method:

Private  void doAcquireShared ( int Arg) {
     // the current thread package into the shared mode Node node, inserted into the main queue at the end of 
    Final Node Node = addWaiter (Node.SHARED);
     Boolean failed = to true ;
     the try {
         // interrupt flag bit 
        Boolean interrupted = to false ;
         for (;;) {
             Final the node P = node.predecessor ();
             // if predecessor node is head node, then the attempt to obtain resources 
            IF (P == head) {
                 int R & lt = tryAcquireShared (Arg);
                 //Means the current thread successfully acquired resource 
                IF (R & lt> = 0 ) {
                     // set the first node and the propagation state (note the different exclusive mode) 
                    setHeadAndPropagate (Node, R & lt); 
                    p.next = null ; // Help the GC 
                    iF (interrupted) 
                        selfInterrupt (); 
                    failed = to false ;
                     return ; 
                } 
            } 
            // should sleep (the same as the exclusive mode, omitted) 
            iF (shouldParkAfterFailedAcquire (P, Node) && 
                parkAndCheckInterrupt ()) 
                interrupted= To true ; 
        } 
    } the finally {
         IF (failed)
             // cancel (the same as the exclusive mode) 
            cancelAcquire (Node); 
    } 
}

doAcquireShared method saves the current node into a thread package shared mode (the SHARED) and inserted into the end of the main queue. Before addWaiter (Node mode) method has been analyzed text, not repeat them.

 

The method and the difference method is that setHeadAndPropagate acquireQueued method, after the current node is set to the head node, there will be propagated (Propagate) Behavior:

private void setHeadAndPropagate(Node node, int propagate) {
    // 记录旧的头节点
    Node h = head; // Record old head for check below
    // 将 node 设置为头节点
    setHead(node);
    /*
     * Try to signal next queued node if:
     *   Propagation was indicated by caller,
     *     or was recorded (as h.waitStatus either before
     *     or after setHead) by a previous operation
     *     (note: this uses sign-check of waitStatus because
     *      PROPAGATE status may transition to SIGNAL.)
     * and
     *   The next node is waiting in shared mode,
     *     or we don't know, because it appears null
     *
     * The conservatism in both of these checks may cause
     * unnecessary wake-ups, but only when there are multiple
     * racing acquires/releases, so most need signals now or soon
     * anyway.
     */
    if (propagate > 0 || h == null || h.waitStatus < 0 ||
        (h = head) == null || h.waitStatus < 0) {
        Node s = node.next;
        // 后继节点为空或共享模式唤醒
        if (s == null || s.isShared())
            doReleaseShared();
    }
}

doReleaseShared:

private void doReleaseShared() {
    /*
     * Ensure that a release propagates, even if there are other
     * in-progress acquires/releases.  This proceeds in the usual
     * way of trying to unparkSuccessor of head if it needs
     * signal. But if it does not, status is set to PROPAGATE to
     * ensure that upon release, propagation continues.
     * Additionally, we must loop in case a new node is added
     * while we are doing this. Also, unlike other uses of
     * unparkSuccessor, we need to know if CAS to reset status
     * fails, if so rechecking.
     */
    for (;;) {
        // 这里的头节点已经是上面设置后的头节点了
        H = the Node head;
         // Since this method has two inlets (setHeadAndPropagate and releaseShared), to consider concurrency control 
        IF (H =! Null && H =! Tail) {
             int WS = h.waitStatus;
             IF (WS == the Node .SIGNAL) {
                 IF (compareAndSetWaitStatus (H, Node.SIGNAL, 0! ))
                     Continue ;             // Loop to Recheck in Cases
                 // wake successor nodes 
                unparkSuccessor (H); 
            } 
            the else  IF (WS == 0 && 
                     ! compareAndSetWaitStatus (H, 0, Node.PROPAGATE))
                 Continue ;                 // Loop ON failed CAS 
        }
         // if the first node constant, out of the loop; otherwise continue circulating 
        IF (H == head)                    // Loop IF head changed 
            BREAK ; 
    } 
}

The method and the method of obtaining the exclusive mode acquire substantially similar, except that the method, after obtaining the resource nodes spread state, i.e., there may continue to wake up the successor node. It is noted that: This method has two inlets and setHeadAndPropagate releaseShared, there may be multiple-threaded operation, concurrency control should be considered.

 

In addition, I am for the node is set to PROPAGATE understanding of the state is not very clear, the online version is also more than one, to be follow-up study to understand to add.

 

2. access to resources in shared mode (interrupt response)

 

This method is similar to acquireShared:

public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

Front tryAcquireShared method has been analyzed, if access to resources fails, perform doAcquireSharedInterruptly method:

Private  void doAcquireSharedInterruptibly ( int Arg)
     throws InterruptedException {
     // the current thread package into the shared mode node, and inserted into the main queue 
    Final the Node Node = addWaiter (Node.SHARED);
     Boolean failed = to true ;
     the try {
         for (;;) {
             Final P = the Node node.predecessor ();
             IF (P == head) {
                 int R & lt = tryAcquireShared (Arg);
                 IF (R & lt> = 0 ) { 
                    setHeadAndPropagate (Node, R & lt); 
                    p.next = null ; // Help the GC 
                    failed = to false ;
                     return ; 
                } 
            } 
            // compared with doAcquireShared, except that where an exception is thrown 
            IF (shouldParkAfterFailedAcquire (P, Node) && 
                parkAndCheckInterrupt ()) 
                the throw  new new InterruptedException (); 
        } 
    } the finally {
         IF (failed) 
            cancelAcquire (Node); 
    } 
}

We can see from the code, acquireSharedInterruptibly method and acquireShared method is almost exactly the same, except that only the former will throw an exception InterruptedException interrupt response; while the latter recorded only flag acquired after the end of the response.

 

3. access to resources in shared mode (interrupt response, and there is a timeout)

 

Code is as follows (the process may be comparatively analyzed to obtain the time-out method in the foregoing exclusive mode):

public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    return tryAcquireShared(arg) >= 0 ||
        doAcquireSharedNanos(arg, nanosTimeout);
}

doAcquireSharedNanos: 

private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (nanosTimeout <= 0L)
        return false;
    final long deadline = System.nanoTime() + nanosTimeout;
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return true;
                }
            }
            nanosTimeout = deadline - System.nanoTime();
            if (nanosTimeout <= 0L)
                return false;
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

The method may be carried out with the method of the timeout wait tryAcquireNanos (int arg, long nanosTimeout) contrast in exclusive mode, both operations are basically the same, no detailed analysis.

 

4. Release resources, Wake node spread state

 

as follows:

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

tryReleaseShared:

protected boolean tryReleaseShared(int arg) {
    throw new UnsupportedOperationException();
}

Front doReleaseShared () method has been analyzed in depth. The present method and release method similar exclusive mode, difference is that "spread" the word.

 

Scene analysis

 

To facilitate understanding the state queues sharing mode and exclusive mode and nodes, a brief analysis of the following example.

The scenario: there were five T0 ~ T4 threads access to resources in chronological order, wherein T2 and T3 shared mode, other modes are exclusive.

 

In this scene analysis: T0 to get to resources (assuming that take a long time), and then re-acquire the T1 ~ T4 fails, it will turn into the main queue. At this time, the main queue of each node in a state diagram is as follows:

After, T0 operation is completed and the release of resources, T1 will wake up. T1 (Exclusive) will be from acquireQueued (final Node node, int arg) cyclic process continues acquisition of the resources, will succeed this time, and T1 is set to the head node (T is removed). At this time, the main queue node diagram is as follows:

In this case, T1 acquired resources and related operations.

 

Then, the operation is complete Tl release resources, and the next node T2, T2 (shared mode) continues from the loop doAcquireShared (int) method wakeup. At this time T2 successful access to resources, the node itself to the head (T1 to be removed), since the sharing mode are successor nodes T3, T1 will thus continue to wake T3; wake-up operation after the same T2 T3, T4, but not shared successor nodes mode, so no further wake. At this time, the node queue state diagram is as follows:

In this case, T2 and T3 simultaneously acquired resources.

 

Then, when both are released resources will wake T4:

T4 access to resources is similar to T1.

 

PS: This scenario is for reference only, only for ease of understanding, please correct if inappropriate.

 

summary

 

This paper analyzes the three ways of access to resources in shared mode, and release operation of resources. They are as follows:

 

1. acquireShared: access to resources shared mode, ignoring interrupts;

2. acquireSharedInterruptibly: shared mode access to resources, interrupt response;

3. tryAcquireSharedNanos: shared mode access to resources, interrupt response, with Overtime;

4. releaseShared: free up resources, Wake successor node, and ensure the dissemination.

 

And a brief analysis of the state of each node in the scene under the primary queue. In addition, there AQS related operations and conditions ConditionObject nested class queue, when it comes to the rear further analysis.

 

To analyze source code alone AQS more boring, the text will bind source ReentrantLock, CountdownLatch other common tools analyzed concurrently.

 

The above-mentioned resolution is reference information and other personal understanding, please correct me if inappropriate.

 

Related Reading:

JDK source code analysis -AbstractQueuedSynchronizer (2)

JDK源码分析-AbstractQueuedSynchronizer(1) 

 

Stay hungry, stay foolish.

PS: 本文首发于微信公众号【WriteOnRead】。

Guess you like

Origin www.cnblogs.com/jaxer/p/11305925.html