AQS detailed explanation

Transfer: http://www.cnblogs.com/waterystone/p/4920797.html

First, an overview

  When it comes to concurrency, you have to talk about ReentrantLock; when it comes to ReentrantLock, you have to talk about AbstractQueued Synchronizer (AQS)!

  Class is its name.Abstract queue synchronizerAQSA set of definitions is defined.Multithreaded access to shared resourcesSynchronizer frameworkMany synchronous class implementations rely on it.Such as commonly used ReentrantLock/Semaphore/CountDownLatch…。

  What is thread synchronization?:Thread synchronization refers to multiple threads in the same process.Coordinate with each otherWork to achieve consistency.

  The following is the directory outline of this article:

    1. Summary
    2. frame
    3. Detailed explanation of source code
    4. Simple application

  If there is anything wrong, please understand and criticize.

  Respect the author’s work and link to the original text for reprint: http://www.cnblogs.com/waterystone/p/4920797.html

  Mobile version accessible: https://mp.weixin.qq.com/s/eyZyzk8ZzjwzZYN4a4H5YA

Two. Frame

  It maintains a volatile int state (representing shared resources) and a FIFO thread waiting queue (which is entered when a multithreaded contention resource is blocked). Here volatile is the core key word, and the meaning of concrete volatile is not described here.. There are three ways to access state:

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

  AQSDefining two ways of sharing resources:Exclusive(Monopoly,Only one thread can execute such as ReentrantLock and Share.Share,Multiple threads can be executed at the same time, such as Semaphore/CountDownLatch.

  Different custom synchronizer contends for different ways to share resources.The custom synchronizer only needs to achieve the access and release of shared resource state when it is implemented.,AQS is already implemented at the top level for specific threads waiting for queue maintenance (such as failing to fetch resources into the queue / wake up the queue, etc.). The implementation of custom synchronizer mainly implements the following methods:

  • isHeldExclusively():Is the thread being exclusive resources? Only condition is needed to implement it.
  • tryAcquire(int):Exclusive way. Try to get resources, success will return to true, failure will return to false.
  • tryRelease(int):Exclusive way. Try to release resources, success will return to true, and failure will return to false.
  • tryAcquireShared(int):Sharing mode. Try to get resources. Negative numbers indicate failure; 0 indicates success, but there are no remaining resources available; positive numbers indicate success, and there are remaining resources.
  • tryReleaseShared(int):Sharing mode. Try to release the resource. If it is allowed to wake up the subsequent waiting node to return to true after release, otherwise it will return false.

  Take ReentrantLock as an example, state is initialized to 0, indicating an unlocked state. When A thread lock (), it calls tryAcquire () to monopolize the lock and state+1. After that, other threads will lose again when they are tryAcquire ().Failed until thread A unlock () reaches state = 0 (that is, releasing the lock), other threads have no chance to acquire the lock. Of course, before releasing the lock, the A thread itself can retrieve the lock repeatedly (the state accumulates), which is the concept of reentrance. But watch how many times you get it.How soon will it be released so that state can return to zero state.

  In CountDownLatch, for example, the task is divided into N sub-threads to execute, and the state is initialized to N (note that N is the same as the number of threads). This N child thread is executed in parallel. Each child thread executes countDown () once, and state will C.AS minus 1. When all the subthreads are executed (that is, state = 0), the main calling thread unpark () is returned from the await () function to continue the rest of the action.

  Generally speaking, custom synchronizers are either exclusive or shared, and they only need to implement one of tryAcquire-tryRelease, tryAcquireShared-tryReleaseShared. But AQSIt also supports two ways, such as ReentrantReadWriteLock, to synchronize and synchronize the user.

Three, source code details

  This section starts with the implementation of AQS’s source code. According to the order of acquire-release and acquireShared-releaseShared.

3.1 acquire(int)

  This method is the top-level entry for threads to get shared resources under exclusive mode. If a resource is acquired, the thread returns directly, otherwise it enters the wait queue until the resource is acquired, and the entire process ignores the impact of the interruption. This is also the semantics of lock (), which is, of course, not limited to lock (). ObtainAfter getting the resources, the thread can execute its critical area code. The following is the source code of acquire ().

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

The function flow is as follows:

    1. tryAcquire()Try to get resources directly, if successful, return directly.
    2. addWaiter()Add the thread to the tail of the waiting queue and mark it as exclusive mode.
    3. acquireQueued()Causes the thread to get resources in the waiting queue, and then gets back to the resource until it has been reached. If it is interrupted during the whole waiting process, it will return to true, otherwise it will return to false.
    4. If the thread is interrupted during the wait, it will not respond. It is only after obtaining resources that the selfInterrupt interruption is interrupted.

  It doesn’t matter if you look at these four abstract functions and see the process a bit hazy. After reading the next analysis, you’ll understand. As the Tang Buddhist monk said in A Journey to the West: When you understand the truth of sacrificing life and righteousness, you will naturally come back to sing this song with me.

3.1.1 tryAcquire(int)

  This method attempts to obtain exclusive resources. If successful, return to true directly, otherwise false will be returned directly. This is exactly the semantics of tryLock (), or that sentence, of course, not just tryLock (). The following is the source of tryAcquire ().Code:

1     protected boolean tryAcquire(int arg) {
2         throw new UnsupportedOperationException();
3     }

What? Direct throw anomaly? What about the function? OKRemember that AQS in the overview is just a framework, and the specific resource acquisition / release mode is handed over to a custom synchronizer?Here it is!!! AQS only defines an interface, and the acquisition of specific resources is implemented by a custom synchronizer (via state get / set / CAS)!! As for the ability to re-enter, can add plug, then see how the specific custom synchronizer to design!!!Of course, custom synchro should consider the impact of thread security when accessing resources.

  This is not defined as abstract because in exclusive mode only tryAcquire-tryRelease is implemented, while in shared mode only tryAcquireShared-tryReleaseShared is implemented. If allDefined as abstract, then each mode has to implement interfaces in another mode. In the final analysis, Doug Lea still stands in the perspective of our developers to minimize unnecessary workload.

3.1.2 addWaiter(Node)

  This method is used to add the current thread to the end of the waiting queue and return to the node where the current thread is located. Or source code:

private Node addWaiter(Node mode) {
    //Constructs nodes in a given pattern. There are two kinds of mode: EXCLUSIVE (exclusive) and SHARED (share).
    Node node = new Node(Thread.currentThread(), mode);
    
    //Try a quick way to end the queue directly.
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    
    //The last step was to join the team through enq.
    enq(node);
    return node;
}

No more, just read the notes. Here we say Node. Node nodes encapsulate each thread accessing the synchronization code, including the thread itself that needs to be synchronized and the state of the thread, such as whether it is blocked, whether it is awakened, whether it has been canceled, etc. Variable waitStAtus represents the wait state currently encapsulated as a Node node, with four values: CANCELLED, SIGNAL, CONDITION, and PROPAGATE.

  • CANCELLED:The value is 1. When a thread waiting in a synchronous queue is timed out or interrupted, the node of the Node needs to be cancelled from the synchronous queue. The waiting status of the node is CANCELLED, that is, the end state, and the node will not change after entering the state.

  • SIGNAL:The value is – 1 and is identified as a successor node in the awakening state. When the thread of the successor node releases the synchronization lock or is cancelled, the thread of the successor node is notified to execute. To put it plainly, a wake-up state notifies a successor node that is identified as SIGNAL as long as the successor node releases the lockThread execution.

  • CONDITION:The value is -2, which is related to Condition, where the node of the identity is located.Waiting queueThe thread of the node waits on Condition, and when other threads call Condition’s signal () method, the node in the CONDITION state willFrom waiting queue to synchronous queue,Waiting for the synchronization lock.

  • PROPAGATE:The value is – 3, which is related to the shared mode in which the thread identifying the node is in a runnable state.

  • 0Status: the value is 0, representing the initialization state.

AQSIn judging the state, waitStatus & gt; 0 is used to denote the canceled state, while waitStatus & lt; 0 is used to denote the valid state.

3.1.2.1 enq(Node)

   This method is used to add node to the queue. The source code is as follows:

private Node enq(final Node node) {
    //CAS"Spin until you join the team.
    for (;;) {
        Node t = tail;
        if (t == null) { // The queue is empty, creating an empty flag node as the head node and pointing tail to it.
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {//Normal process, put in queue.
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

 

If you’ve seen the source code for the AtomicInteger. getAndIncrement () function, you can see the essence of this code at a glance.CASSpin volatile variable,It’s a classic usage. I don’t know much about it. I’ll go to Baidu myself.

3.1.3 acquireQueued(Node, int)

  OK,With tryAcquire () and addWaiter (), the thread failed to get the resource and was placed at the end of the wait queue. Smart, you should immediately think of what the next thread should do.Get into the wait state and rest until the other threads completely release the resources and wake up, get the resources themselves, and then go do what they want to do.。Yes, that’s it! Is it a little similar to queuing up in hospitals for ~~acquireQueued?Wait for the queue to take the number (there’s nothing else to rest in the middle) until you get the number.。This function is very critical.

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;//Whether the markings were successfully delivered to the resource
    try {
        boolean interrupted = false;//Is the tag interrupted during the waiting process?//Another spin!
        for (;;) {
            final Node p = node.predecessor();//Get the forerunner//If the predecessor is head, which means that the node is already the second, then it is qualified to try to get resources (perhaps the boss has released the resources to wake up his own, of course, may be interrupt).
            if (p == head && tryAcquire(arg)) {
                setHead(node);//After getting the resources, point head to the node. So the point bar that head refers to is the node or null that currently gets the resource.
                p.next = null; // setHeadIn order to facilitate GC to recycle the previous head node, node. prev is set to null and head. next is set to null. It means that the nodes that have taken the resources out of the team!
                failed = false;
                return interrupted;//Is it interrupted during the waiting process?
            }
            
            //If you can take a break, go into the waiting state until you get unpark ().
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;//If the waiting process is interrupted, even if only once, mark the interrupted as true.
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

Now, instead of summarizing the function flow of acquireQueued (), let’s look at what shouldParkAfterFailedAcquire () and parkAndCheckInterrupt () do.

3.1.3.1 shouldParkAfterFailedAcquire(Node, Node)

  This method is mainly used to check the state to see if you can really go to rest (enter the waiting state, if the thread state transition is not familiar, you can refer to my last Thread to explain), in case the front of the queue threads have given up just standing, that may be, right!

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;//Get the precursor.
    if (ws == Node.SIGNAL)
        //If you have told the forerunner to take your notice, you can rest at ease.
        return true;
    if (ws > 0) {
        /*
         * If the forerunner gives up, keep searching until you find the nearest normal waiting state, next to it.* Note: The abandoned nodes, being "jammed" in front of them by themselves, are equivalent to forming a non-reference chain, which will be guarded later.Uncle drives away (GC recycling)!*/
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
         //If the forerunner is normal, set the state of the forerunner to SIGNAL, and tell it to notify yourself when it has taken the number. It is possible to fail. Someone may have just released it.
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

Throughout the process, if the state of the precursor node is not SIGNAL, then they can not rest at ease, need to find a rest at ease, and can try again to see if there is a chance to take their turn.

3.1.3.2 parkAndCheckInterrupt()

  If the thread finds a safe rest point, it can rest assured. This method is to let the thread rest and really enter the waiting state.

1 private final boolean parkAndCheckInterrupt() {
2     LockSupport.park(this);//Calling Park () causes the thread to enter the waiting state.
3     return Thread.interrupted();//If you are awakened, check if you are interrupted.
4 }

park()The current thread will go into the waiting state. In this state, there are two ways to wake up the thread: 1) by unpark (); 2) by interrupt (). (again, if the thread state conversion is not familiar, you can refer to the Thread I wrote in detail). needNote that Thread.interrupted () clears the interrupt bits of the current thread.  

3.1.3.3 Summary

  OK,Looking at shouldParkAfterFailedAcquire () and parkAndCheckInterrupt (), let’s go back to acquireQueued () and summarize the flow of the function.

  1. After entering the team, check the status and find a safe rest point.
  2. Call park () into the waiting state, wait for unpark () or interrupt () to wake yourself up.
  3. Wake up to see if you are qualified to get the number. If you get it, the head points to the current node and returns whether the entire process from queuing to getting the number has been interrupted; if not, proceed with Flow 1.

 

3.1.4 Summary

  OKOK,acquireQueued()After analysis, let’s go back to acquire () again. And paste it with its source code.

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

Let’s summarize the process.

  1. The tryAcquire () that calls the custom synchronizer tries to get resources directly, and returns directly if successful.
  2. Without success, addWaiter () adds the thread to the tail of the waiting queue and marks it as exclusive mode.
  3. acquireQueued()Make the thread rest in the wait queue and try to get the resource when there is an opportunity (unpark ()). Gets the resource before it returns. If it is interrupted during the whole waiting process, it will return to true, otherwise it will return to false.
  4. If the thread is interrupted during the wait, it will not respond. It is only after obtaining resources that the selfInterrupt interruption is interrupted.

Since this function is the top priority, I will summarize it with the flowchart.

So far, the acquire () process has finally come to an end. This is the ReentrantLock. lock () process, do not believe you look at its lock () source, the entire function is an acquire (1)!!!

 

3.2 release(int)

   The last section has finished acquire (). In this section, we will talk about its reverse operation release (). This method is the top-level entry of threads to release shared resources under exclusive mode. It releases the specified amount of resources, and if it is completely released (that is, state=0), it will wake up.Wait for other threads in the queue to get resources. This is also the semantics of unlock (), which is, of course, not limited to unlock (). The following is the source code of release ().

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;//Find the head node.
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);//Wake up the next thread in the waiting queue.
        return true;
    }
    return false;
}

Logic is not complicated. It calls tryRelease () to release resources. One thing to note is thatIt is based on the return value of tryRelease () to determine whether the thread has completed the release of resources. So the custom synchronizer should be clear when designing tryRelease ().

3.2.1 tryRelease(int)

  This method attempts to release the specified amount of resources. The following is the source code of tryRelease ().

1 protected boolean tryRelease(int arg) {
2     throw new UnsupportedOperationException();
3 }

Like tryAcquire (), this method is implemented by a custom synchronizer in the exclusive mode. Normally, tryRelease () will succeed, because this is exclusive mode, and the thread releases the resource, so it must have got the exclusive resource, directly subtracting the phaseThe amount of resources available is (state-=arg), and there is no need to consider thread safety. But pay attention to its return value, as mentioned above.release()It is based on the return value of tryRelease () to determine whether the thread has completed the release of resources.So when the self-defined synchronizer is implemented, if the resource has been completely released (state = 0), it returns true, otherwise it returns false.

3.2.2 unparkSuccessor(Node)

  This method is used to wake up the next thread in the waiting queue. The following is the source code:

private void unparkSuccessor(Node node) {
    //Here, node is usually the node where the current thread is located.
    int ws = node.waitStatus;
    if (ws < 0)//The node state where the thread is located is allowed to fail.
        compareAndSetWaitStatus(node, ws, 0);

    Node s = node.next;//Find the next node to wake up, s.
    if (s == null || s.waitStatus > 0) {//If it is empty or cancelled
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)//From here we can see that the nodes of <, =0 are valid nodes.
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);//awaken
}

This function is not complicated. To sum up in one sentence:Use unpark () to wake up the outstanding thread in the waiting queue.,Here we also use s to express it. At this point, again associated with acquireQueued (), s wakes up and enters if (p = = head & amp; & amp; try Acquire (arg)) judgment (even p! = head)It doesn’t matter. It will go back to shouldParkAfterFailedAcquire () to find a security point. Now that s is already the first thread in the wait queue that hasn’t been abandoned, pass shouldParkAfterFailedAcThe next spin P == head is set up, and then S sets itself as the header benchmark node, indicating that he has got the resource, and acquire () returns! And thEN, DO what you WANT!

3.2.3 Summary

  release()It is the top-level entry of threads to release shared resources under exclusive mode. It frees a specified amount of resources, and if it is completely freed (state = 0), it wakes up other threads in the waiting queue to get resources.

3.3 acquireShared(int)

  This method is the top-level entry for threads to get shared resources in shared mode. It fetches a specified amount of resources, returns success directly, and enters the wait queue when the acquisition fails, ignoring interruptions throughout the process until the resources are acquired. The following is the source code of acquireShared ().

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

Here, tryAcquireShared () still needs a custom synchronizer to implement. But AQS has defined the semantics of its return value: a negative value represents a failure to retrieve; a zero represents a success, but no remaining resources; a positive number indicates success, and there are remaining resources, as well as other threads.You can get it. So the process of acquireShared () here is:

    1. tryAcquireShared()Try to get resources, and success returns directly.
    2. Failure enters the waiting queue through doAcquireShared () and does not return until the resource is acquired.

3.3.1 doAcquireShared(int)

  This method is used to add the current thread to the end of the wait queue to rest until other threads release resources to wake themselves up and successfully get the corresponding amount of resources before returning. The following is the source code of doAcquireShared ().

private void doAcquireShared(int arg) {
    final Node node = addWaiter(Node.SHARED);//Queue end
    boolean failed = true;//Sign of success
    try {
        boolean interrupted = false;//The sign that was interrupted during the waiting process.
        for (;;) {
            final Node p = node.predecessor();//precursor
            if (p == head) {//If you go to the next head, because the head is the thread that gets the resource, the node is awakened, probably because the head runs out of the resource to wake itself up
                int r = tryAcquireShared(arg);//Try to get resources
                if (r >= 0) {//Success
                    setHeadAndPropagate(node, r);//Point head to yourself, and the remaining resources can wake up again after the thread.
                    p.next = null; // help GC
                    if (interrupted)//If the waiting process is interrupted, the interrupt will be interrupted.
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            
            //Determine the state, find the security point, enter the waiting state, wait for unpark () or interrupt ().
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

 

Does wood feel similar to acquireQueued ()? Yes, in fact, the process is not very different. Just put the selfInterrupt () to doAcquireShared () and the exclusive mode to acquirEQueued (), in fact, is the same. I don’t know what Doug Lea thinks.

  In contrast to the exclusive mode, it is also important to note that only when the thread is head. next (“the second person”) will the attempt be made to get the resource, and if there is anything left, the subsequent teammates will be awakened. Then the problem is coming. If the boss uses up, he releases 5 resources, and the second one needs 6.1, and 2 for old four. The boss first wakes up the second child. When the second one sees the resources are not enough, is he giving the resources to the old three or not? The answer is No. The second will continue to park () waiting for other threads to release resources, and will not even wake up old three and old four. Exclusive mode, only one at the same time.It’s not impossible for a thread to execute, but in shared mode, multiple threads can execute simultaneously, and now because the second thread has a large demand for resources, the third and fourth threads, who are small in number, are stuck. Of course, this is not a problem, but AQS is sure to wake up in strict order.Flat, but reduced concurrency).

 

3.3.1.1 setHeadAndPropagate(Node, int)

private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; 
    setHead(node);//headPoint to yourself//If there is still a surplus, wake up the next neighbor thread.
    if (propagate > 0 || h == null || h.waitStatus < 0) {
        Node s = node.next;
        if (s == null || s.isShared())
            doReleaseShared();
    }
}

This method is a step on the basis of setHead ().That is, when you wake up, if the conditions are met (such as the remaining resources), you will wake up the subsequent nodes, after all, is the sharing mode!

  doReleaseShared()We keep the next section in releaseShared ().

 

3.3.2 Summary

  OK,So far, acquireShared () is coming to an end. Let’s sort out its process again.

    1. tryAcquireShared()Try to get resources, and success returns directly.
    2. Failure enters the waiting queue Park () through doAcquireShared () until unpark () / interrupt () and the resource is successfully obtained. The whole waiting process is also ignoring interruptions.

  In fact, it is much the same as acquire ().After getting the resources, they will wake up the operation of their successors.

3.4 releaseShared()

  I’ve finished with acquireShared () in the previous section, but let’s talk about its anti-release Shared (). This method is the top entrance of threads sharing resources in shared mode. It releases the specified amount of resources, if it is successfully released and allowed to call.The wake-up waiting thread wakes up other threads in the waiting queue to get resources. The following is the source code of releaseShared ().

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {//Try to release resources
        doReleaseShared();//Wake up subsequent nodes
        return true;
    }
    return false;
}

The process of this method is relatively simple.After releasing the resources, wake up the successor.。Similar to release () in exclusive mode, there is a slight need to note:TryRelease () in exclusive mode returns true to wake up other threads after the resource is completely released (state = 0),This is mainly based on the re entrant consideration.There is no requirement for releaseShared () in shared mode.,The essence of shared mode is to control a certain number of threads to execute concurrently, so the threads with resources can wake up the subsequent waiting nodes when some resources are released. For example, the total amount of resources is 13, A (5) and B (7) get the resources to run concurrently, and C (4) comes with only one resource to wait for. A releases two resource quantities during the run, and then tryReleaseShared (2) returns to true to wake up C. C sees that only three are still not waiting; then B releases two, and tryReleaseShared (2) returns to true to wake up C.C can see that 5 of them are enough for their own use, and then C can run with A and B. The tryReleaseShared () of the ReentrantReadWriteLock read lock returns true only if the resource is completely released (state = 0).A custom synchronizer can determine the return value of tryReleaseShared () according to the need.

3.4.1 doReleaseShared()

  This method is mainly used to wake up successors.。The following is its source code:

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;
                unparkSuccessor(h);//Wake up
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;
        }
        if (h == head)// headundergo changes
            break;
    }
}

3.5 Summary

  In this section we detail the source code for acquisition-release (acquire-release, acquire-release Shared) resources under both exclusive and shared modes, and we believe you all know that. It is worth noting that acquire (And acquireSahred () two methods, the thread in the waiting queue is ignored interruption. AQS also supports acquireInterruptibly () /acquireSharedInterruptibly in response to interruption.() That is, the corresponding source code here is similar to acquire () and acquire Sahred (), which is not explained here.

 

Four, simple application

  Through the study of the preceding chapters, I believe you have basically understood the principle of AQS. Here is a copy of a paragraph in the “framework” section.

  Different custom synchronizer contends for different ways to share resources.The custom synchronizer only needs to achieve the access and release of shared resource state when it is implemented.,AQS is already implemented at the top level for specific threads waiting for queue maintenance (such as failing to fetch resources into the queue / wake up the queue, etc.). The implementation of custom synchronizer mainly implements the following methods:

  • isHeldExclusively():Is the thread being exclusive resources? Only condition is needed to implement it.
  • tryAcquire(int):Exclusive way. Try to get resources, success will return to true, failure will return to false.
  • tryRelease(int):Exclusive way. Try to release resources, success will return to true, and failure will return to false.
  • tryAcquireShared(int):Sharing mode. Try to get resources. Negative numbers indicate failure; 0 indicates success, but there are no remaining resources available; positive numbers indicate success, and there are remaining resources.
  • tryReleaseShared(int):Sharing mode. Try to release the resource. If it is allowed to wake up the subsequent waiting node to return to true after release, otherwise it will return false.

  OK,Next, let’s take the Mutex in the AQS source for example, and talk about the simple application of AQS.

4.1 Mutex(Mutual exclusion lock

  MutexIs a non reentrant mutex implementation. The lock resource (state in AQS) has two states: 0 indicates no lock, and 1 indicates lock. Below is the core source of Mutex:

class Mutex implements Lock, java.io.Serializable {
    // Custom synchronizer
    private static class Sync extends AbstractQueuedSynchronizer {
        // Determining whether to lock state
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        // Try to get resources and return immediately. True is returned to success, otherwise false.
        public boolean tryAcquire(int acquires) {
            assert acquires == 1; // Only 1 quantities can be defined here.
            if (compareAndSetState(0, 1)) {//stateSet 1 for 0 and not re enter!
                setExclusiveOwnerThread(Thread.currentThread());//Set to exclusive resource for current thread
                return true;
            }
            return false;
        }

        // Try to release resources and return immediately. Success is true, otherwise false.
        protected boolean tryRelease(int releases) {
            assert releases == 1; // Limited to 1 quantities.
            if (getState() == 0)//Since it is released, it must have been occupied. Just for insurance, multi level judgment!
                throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);//Release resources and abandon occupancy
            return true;
        }
    }

    // The implementation of true synchronization classes depends on the custom synchronizer inherited from AQS!
    private final Sync sync = new Sync();

    //lock<-->acquire。The two are semantically the same: get resources, even if they wait until they succeed.
    public void lock() {
        sync.acquire(1);
    }

    //tryLock<-->tryAcquire。Both semantics are the same: try to get resources and ask for immediate return. Success is true and failure is false.
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    //unlock<-->release。The two languages are like: releasing resources.
    public void unlock() {
        sync.release(1);
    }

    //Does the lock occupy state?
    public boolean isLocked() {
        return sync.isHeldExclusively();
    }
}

 

Synchronization classes are usually defined as internal classes for their own use when implementing sync.;The synchronization class itself (Mutex) implements an interface and services externally. Of course, the implementation of interfaces depends directly on sync, and there is a corresponding relationship in semantics.Sync only uses tryAcquire-tryRelelase, which implements the acquisition-release mode of resource state. As for the queuing, waiting, waking-up of threads, the upper AQS has been implemented.,We need not care.

  Apart from Mutex, ReentrantLock / CountDownLatch / Semphore are implemented in much the same way as other synchronization classes, except for tryAcquire – tryRelelase。With this in mind, the core of AQS is broken.

  Simply put, Mutex is exclusive, with only one resource available, and Semaphore is also exclusive, but it defines multiple objects of resources available. (concurrent can’t find the Mutex class under the package)

  OK,At this point, the explanation of the whole AQS will come to a close. I hope this article can be used for reference by students who study Java concurrent programming. There are some mistakes in the middle, and we welcome discussion and correction.

Author: water rock

    

Source: http://www.cnblogs.com/waterystone/

Leave a Reply

Your email address will not be published. Required fields are marked *