I. Overview
Talking about concurrency, having to talk about Reentrantlock, and talking about Reentrantlock, have to talk abstractqueuedsynchronized (AQS)!
Class such as its name, abstract queue-type Synchronizer, AQS defines a set of multi-threaded access to shared resources of the Synchronizer framework, many synchronous class implementations are dependent on it, such as the common reentrantlock/semaphore/countdownlatch ....
The following is the directory outline for this article:
-
- Overview
- Framework
- SOURCE Detailed
- Simple Application
If there are any shortcomings, please understand and criticize, I appreciate it.
Please respect the author's labor results, reprint please indicate the original link: http://www.cnblogs.com/waterystone/p/4920797.html
Second, the framework
It maintains a volatile int state (representing shared resources) and a FIFO thread waiting queue (this queue is entered when a multithreaded contention resource is blocked). Here volatile is the core keyword, the semantics of specific volatile, not described here. There are three ways to access state:
- GetState ()
- SetState ()
- Compareandsetstate ()
AQS defines two ways to share resources: Exclusive (exclusive, only one thread can execute, such as Reentrantlock) and share (shared, multiple threads can execute concurrently, such as Semaphore/countdownlatch).
Different custom synchronizers compete for shared resources in different ways. The custom Synchronizer only needs to realize the acquisition and release of the shared resource state when it is implemented, as long as the specific thread waits for the queue to be maintained (such as getting the resource failed to join/wake up the team, etc.), the AQS has been implemented at the top level. There are several main ways to implement a custom Synchronizer implementation:
- Isheldexclusively (): Whether the thread is monopolizing the resource. Only use the condition to realize it.
- Tryacquire (int): Exclusive mode. Attempts to get a resource, success returns TRUE, and failure returns false.
- Tryrelease (int): Exclusive mode. Attempt to free the resource, success returns TRUE, and failure returns false.
- tryacquireshared (int): How to share. Try to get the resource. A negative number indicates a failure; 0 indicates success, but there is no remaining available resources; A positive number indicates success and there are remaining resources.
- tryreleaseshared (int): How to share. Attempt to free the resource, success returns TRUE, and failure returns false.
In Reentrantlock, for example, state is initialized to 0, which indicates an unlocked status. A thread lock () will call Tryacquire () to monopolize the lock and will state+1. Thereafter, the other thread Tryacquire () fails until the A thread unlock () to state=0 (that is, releasing the lock), and the other thread has the opportunity to acquire the lock. Of course, before releasing the lock, the a thread itself can get the lock repeatedly (state will accumulate), which is the concept of reentrant. Be aware, however, how many times it will be released so that the state can return to 0 states.
In the case of Countdownlatch, the task is divided into N sub-threads to execute, and the state is initialized to n (Note that n is consistent with the number of threads). This n sub-thread is executed in parallel, once each child thread finishes executing countdown () once, state CAs minus 1. When all child threads are executed (that is, state=0), the thread will be Unpark (), and then the keynote thread will return from the await () function to continue with the remainder action.
In general, custom synchronizers are either exclusive or shared, and they only need to implement one of the Tryacquire-tryrelease, tryacquireshared-tryreleaseshared. However, Aqs also supports both exclusive and shared implementations of the custom Synchronizer, such as Reentrantreadwritelock.
Third, the source detailed
This section begins by explaining the source code implementation of AQS. According to the order of Acquire-release and acquireshared-releaseshared.
3.1 Acquire (int)
This method is the exclusive mode thread that gets the top-level entry for the shared resource. If the resource is fetched, the thread returns directly, otherwise it enters the wait queue until the resource is fetched, and the entire process ignores the impact of the interruption. This is also the semantics of Lock (), which is not limited to lock (), of course. Once the resource is fetched, the thread can execute its critical section code. Here is the source code for 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:
-
- Tryacquire () Try to get resources directly, and if successful, return directly;
- Addwaiter () joins the thread to the tail of the waiting queue and marks it as exclusive;
- Acquirequeued () causes the thread to get the resource in the wait queue until it has been fetched to the resource before returning. Returns true if the entire wait is interrupted, otherwise false.
- If the thread has been interrupted during the wait, it is not responding. Just get the resources before you interrupt yourself Selfinterrupt () and break up.
At this time alone with these 4 abstract function to see the process is a little hazy, it does not matter, after reading the next analysis, you will understand. Like the "Big Talk Westward Tour" in the Tang Priest said: "When you understand the truth of just take care, you will naturally come back and I sing this song."
3.1.1 Tryacquire (int)
This method attempts to obtain an exclusive resource. If it succeeds, it returns true directly, otherwise it returns false directly. This is also the semantics of Trylock (), or that sentence, of course, is not limited to Trylock (). The following is the source code for Tryacquire ():
1 protected Boolean tryacquire (int arg) {2 throw new Unsupportedoperationexception (); 3 }
What the? Direct throw exception? What about the function? Well, remember that the AQS in the overview is just a framework, the acquisition/release of specific resources to the custom Synchronizer to achieve it? here it is!!! Aqs here only defines an interface, the acquisition of specific resources by the custom Synchronizer to achieve (through the state of the GET/SET/CAS)!!! As for the ability to re-enter, can not be, that depends on the specific custom Synchronizer how to design!!! The custom Synchronizer, of course, takes into account the impact of thread safety when accessing resources.
This is not defined as abstract, because exclusive mode is only implemented Tryacquire-tryrelease, and the shared mode is only implemented tryacquireshared-tryreleaseshared. If all are defined as abstract, then each pattern also implements an interface in another mode. In the end, Doug Lea stands in the perspective of our developers and minimizes unnecessary work.
3.1.2 Addwaiter (Node)
This method is used to join the current thread to the end of the queue and to return the node where the current thread is located. or on the source bar:
1 Private node Addwaiter (node mode) {2 //constructs nodes in a given pattern. Mode is available in two ways: EXCLUSIVE (exclusive) and shared (shared) 3 node node = new node (thread.currentthread (), mode), 4 5 //Try a quick way to put it directly at the end of the team. 6 Node pred = tail; 7 if (pred! = null) {8 node.prev = pred; 9 if (Compareandsettail (pred, Node)) {10
pred.next = node;11 return node;12 }13 }14 //Previous step failed to queue by Enq. Enq (node), + return node;18}
No more talking, just read the notes.
3.1.2.1 Enq (Node)
This method is used to join node to the end of the queue. The source code is as follows:
1 Private node Enq (final node node) {2 //cas "Spin" until successfully joined the end of Team 3 for (;;) {4 Node t = tail; 5 if (t = = null) {//queue is empty, create an empty flag node as head node, and point tail to it. 6 if (Compareandsethead (new Node ())) 7 tail = head; 8 } else {//normal process, put into team tail 9 Node.prev = t;10 if (comp Areandsettail (t, node)) { T.next = node;12 return t;13 }14}15 }16}
If you have seen the atomicinteger.getandincrement () function source code, then I believe you can see the essence of this piece. CAS spin volatile variable is a classic usage. Still do not know very well, go to Baidu for a bit.
3.1.3 acquirequeued (Node, int)
OK, with Tryacquire () and Addwaiter (), the thread gets a resource failure and has been placed at the end of the wait queue. Smart you should immediately be able to think of the next part of the thread what to do: Go to the waiting state to rest, until the other threads to completely release the resources to wake up their own resources, and then can go to do what they want to do . Yes, that's it! Is it a little similar to the hospital line? ~~acquirequeued () is to do this: queue up in the waiting queue to take the number (no other things in the middle to rest), until the number and then return . This function is very critical, or the source bar:
1 Final boolean acquirequeued (final node node, int arg) {2 Boolean failed = true;//Tag successfully received resource 3 try {4 Boolean interrupted = false;//flag is interrupted during the wait Process 5 6//is another "spin"! 7 for (;;) {8 final Node p = node.predecessor ();//Get the Precursor 9//If the precursor is head, that node is a second, then it is eligible to try to get the resource (perhaps the boss wakes up his own resources, Of course, it could be interrupt.) if (p = = head && tryacquire (ARG)) {One sethead (node);//The head points to the node when the resource is obtained. So the point that head refers to is the node that is currently acquiring the resource, or null. P.next = null; Node.prev has been set to NULL in Sethead, where head.next is reset to NULL to facilitate GC reclamation of previous head nodes. It means that you have to take the resources out of the knot before the team! Failed = false;14 return interrupted;//Returns whether it was interrupted during the wait 15}16 17 If you can rest, enter the waiting state until you are Unpark () if (Shouldparkafterfailedacquire (p, node) &&19 Parkandcheckinterrupt ()) interrupted = true;//If the wait process is interrupted, even if only once, interrupted is marked as True21 }22 } finally {failed) cancelacquire (node); 25}26}
Here, let's not rush to summarize the acquirequeued () of the function flow, first look at Shouldparkafterfailedacquire () and Parkandcheckinterrupt () specifically what to do.
3.1.3.1 Shouldparkafterfailedacquire (node, node)
This method is mainly used to check the status, to see if they can really go to rest (into the waiting state, if the thread state conversion is not ripe, you can refer to my previous write thread in detail), in case the queue front of the threads have given up just blindly standing, that also maybe, right!
1 private static Boolean Shouldparkafterfailedacquire (node pred, node node) {2 int ws = pred.waitstatus;//Gets the status of the precursor 3
if (ws = = node.signal) 4 //If the precursor has been told to notify himself after taking the number, then you can rest at ease 5 return true; 6 if (ws > 0) {7/ * 8 * If the precursor has given up, then keep looking forward until you find the most recent state of waiting, side-by, behind it. 9 * Note: Those abandoned nodes, due to their own "" "into their front, they are equivalent to the formation of a non-referential chain, will later be removed by the security Uncle (GC recycling)! Ten */11 do { Node.prev = pred = pred.prev;13 } while (Pred.waitstatus > 0); pred.next = nod E;15 } else { //If the precursor is normal, set the status of the precursor to signal, telling it to notify itself after taking the number. It is possible to fail, someone may have just released it! compareandsetwaitstatus (pred, WS, node.signal); }19 return false;20}
The whole process, if the state of the precursor node is not signal, then you can not rest assured that you need to find a peace of mind to rest, at the same time can try to see if there is a chance to take their own number.
3.1.3.2 Parkandcheckinterrupt ()
If the thread is looking for a safe break, then you can rest at ease. This method is to let the thread go to rest and really enter the waiting state.
1 Private Final Boolean parkandcheckinterrupt () {2 Locksupport.park (this);//Call Park () to bring the thread into waiting State 3 return Thread.interrupted ();//If awakened, see if you are interrupted. 4}
Park () causes the current thread to enter the waiting state. In this state, there are two ways to wake up the thread: 1) being Unpark (); 2) being interrupt (). (Again, if the thread state conversion is not ripe, you can refer to my written thread detailed). It should be noted that thread.interrupted () clears the interrupt mark bit for the current thread.
3.1.3.3 Summary
OK, look at Shouldparkafterfailedacquire () and Parkandcheckinterrupt (), now let's go back to acquirequeued () and summarize the specific flow of the function:
- After the node enters the tail of the team, check the status and find the safe resting point;
- Call Park () to enter the waiting state, waiting for Unpark () or interrupt () to wake themselves up;
- After being awakened, see if you are qualified to get the number. If you get it, head points to the current node and returns whether the entire process from the queue to the number has been interrupted, and if not, proceed to process 1.
3.1.4 Summary
Okok,acquirequeued () After the analysis, we go back to acquire ()! Then put it on the source bar:
1 Public final void acquire (int arg) {2 if (!tryacquire (ARG) &&3 acquirequeued (Addwaiter ( node.exclusive), Arg) 4 selfinterrupt (); 5}
Let's summarize its process:
- The Tryacquire () that invokes the custom Synchronizer attempts to fetch the resource directly, and returns if successful;
- unsuccessful, Addwaiter () joins the thread to the tail of the waiting queue and marks it as exclusive;
- Acquirequeued () causes the thread to rest in the waiting queue, and when there is a chance (it is its turn, it will be Unpark ()) to try to get the resource. The resource is not returned until it has been obtained. Returns true if the entire wait is interrupted, otherwise false.
- If the thread has been interrupted during the wait, it is not responding. Just get the resources before you interrupt yourself Selfinterrupt () and break up.
Since this function is of the heaviest weight, I'll summarize it with the flowchart:
At this point, the process of acquire () finally came to an end. This is the process of reentrantlock.lock (), do not believe you to see its lock () source bar, the whole function is a acquire (1)!!!
3.2 Release (int)
The previous section has finished acquire (), and this section is about its anti-operation release (). This method is exclusive mode for the thread to release the top-level entry for the shared resource. It frees the specified amount of resources, and if it is completely freed (that is, state=0), it wakes up other threads in the wait queue to get the resources. This is also the semantics of unlock (), not limited to unlock (), of course. Here is the source of release ():
1 Public Final Boolean release (int arg) {2 if (Tryrelease (ARG)) {3 node H = head;//Find the head node 4 if (h! = NULL && Amp H.waitstatus! = 0) 5 unparksuccessor (h);//Wake up the next thread in the wait queue 6 return true;7 }8 return false;9}
Logic is not complicated. It calls Tryrelease () to free up resources. One thing to note is that it is based on the return value of Tryrelease () to determine if the thread has finished releasing the resource! So the custom Synchronizer should be clear when designing tryrelease ()!!
3.2.1 Tryrelease (int)
This method attempts to release a specified amount of resources. Here is the source code for Tryrelease ():
1 protected boolean tryrelease (int arg) {2 throw new Unsupportedoperationexception (); 3}
Like Tryacquire (), this method is implemented by a custom Synchronizer that requires exclusive mode. Normally, Tryrelease () will succeed, because this is an exclusive mode, and the thread frees up resources, then it must have been given the exclusive resources, directly minus the corresponding amount of resources (STATE-=ARG), and does not need to consider thread safety issues. But note that its return value, mentioned above,release () is based on the return value of Tryrelease () to determine whether the thread has finished releasing resources! so the self-tuning synchronizer, when implemented, returns true if the resource (state=0) has been completely freed, otherwise false.
3.2.2 Unparksuccessor (Node)
This method is used to wake up the next thread in the wait queue. Here is the source code:
1 private void Unparksuccessor (node node) {2 //Here, node is typically the node where the current thread resides. 3 int ws = Node.waitstatus; 4 if (WS < 0)//0 The node state where the current thread is located, allowing failure. 5 Compareandsetwaitstatus (node, WS, 0); 6 7 node s = node.next;//Find the next node that needs to be awakened s 8 if (s = = NULL | | s.waitst ATUs > 0) {//If NULL or canceled 9 s = null;10 for (node t = tail; t! = null && t! = Node; t = t.prev) One if (t . waitstatus <= 0)//From here can be seen, <=0 node, are also effective nodes. -S = t;13 }14 if (s! = null) Locksupport.unpark (s.thread);//Wake 16}
This function is not complex. In a nutshell: wake up the waiting queue with Unpark () to the front of the non-abandoned thread , here we also use S to express it. At this point, after the acquirequeued () is connected, S is awakened, the judgment of the IF (p = = head && tryacquire (ARG)) is entered (even if P!=head does not matter, It will then enter Shouldparkafterfailedacquire () to find a safe spot. Since S is already waiting for the front of the queue to abandon the thread, then through the Shouldparkafterfailedacquire () adjustment, S will inevitably run to the head of the next node, the next spin p==head is established), Then s set itself as the head benchmark node, indicating that he has acquired the resources, acquire () also returned!! And then, does what want!
3.2.3 Summary
Release () is a top-level entry for exclusive mode thread release shared resources. It frees the specified amount of resources, and if it is completely freed (that is, state=0), it wakes up other threads in the wait queue to get the resources.
3.3 acquireshared (int)
This method is a shared-mode thread that gets the top-level entry for the shared resource. It gets the specified amount of resources, the success is returned directly, the failure gets into the waiting queue until the resource is fetched, and the entire process ignores the interrupt. Here is the source code for 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: Negative values mean get failed, 0 for success, no remaining resources, positive for success, remaining resources, and other threads to fetch. So the process of acquireshared () here is:
-
- Tryacquireshared () Attempts to obtain resources, and succeeds returns directly;
- Failure passes through doacquireshared () into the wait queue until the resource is fetched to return.
3.3.1 doacquireshared (int)
This method is used to join the current thread to the end of the wait queue until the other thread frees the resource to wake itself and then returns after it has successfully obtained the appropriate amount of resources. Here is the source code for doacquireshared ():
1 private void doacquireshared (int arg) {2 final node node = addwaiter (node.shared);//Join Queue Trailing 3 Boolean failed = true;//whether the successful flag 4 try {5 Boolean interrupted = false;//is interrupted during the wait process if the flag 6 for (;;) {7 final Node p = node.predecessor ();//precursor 8 if (p = = head) {//If Next to head, because head is the thread that gets the resource, Node is called Wake up, it's probably the head. 9 int r = tryacquireshared (ARG) used to wake up the resource,//try to get the resource if (r >= 0) {//Success 11 Setheadandpropagate (node, r);//point the head to itself, and the remaining resources can wake up again after the thread p.next = null; Help GC13 if (interrupted)//If the wait process has been interrupted, this will interrupt the completion. Selfinterrupt (); failed = false;16 return;17 }18}19 20//Judging status, looking for safety points, entering waiting state, waiting to be unpark () or interrupt () if ( Shouldparkafterfailedacquire (p, node) &&22 parkandcheckinterrupt ()) 23 interrupted = true;24}25} finally {if (failed) cancelacquire (node); 28}29}
Is there any wood that is similar to acquirequeued ()? Yes, the process is not much different. But here the Selfinterrupt () is put into the doacquireshared (), and the exclusive mode is placed outside the acquirequeued (), in fact all the same, do not know what Doug Lea think.
Compared to the exclusive mode, there is one more thing to note is that only when the thread is Head.next ("Dick"), it will try to get the resources, there will be the rest of the words to wake up the teammates. Then the problem comes, if the eldest brother released after the release of 5 resources, and the second needs 6, old three need 1, the old four need 2. Because the eldest brother first wake up the second, the second look at the resources not enough for their own use to continue Park (), but also not to wake up old three and old four. Exclusive mode, only one thread at a time to execute, this is not possible, but in the shared mode, multiple threads can be executed simultaneously, now because the second resource demand, and the back of the small amount of old three and old four also stuck.
3.3.1.1 setheadandpropagate (Node, int)
1 private void Setheadandpropagate (node node, int propagate) {2 node H = head; 3 sethead (node);//head point to himself 4 //If there is still amount left, continue to wake the next neighbor Thread 5 if (Propagate > 0 | | h = = NULL | | H.waitstatus < 0) {6 Node s = node.next; 7 if (s = = NULL | | s.isshared ()) 8 doreleaseshared (); 9 }10}
This method in the Sethead () on the basis of a step more, is to wake up at the same time, if the conditions meet (for example, there are remaining resources), but also to awaken the successor node, after all, is a shared mode!
Doreleaseshared () We keep the next section of the releaseshared () in terms of.
3.3.2 Summary
OK, so far, acquireshared () will be over. Let's comb the flow of it:
- Tryacquireshared () Attempts to obtain resources, and succeeds returns directly;
- Failure passes through doacquireshared () into the wait Queue Park () until it is Unpark ()/interrupt () and successfully fetched to the resource before returning. The entire wait process also ignores interrupts.
In fact, with acquire () the process is much the same, just a lot of their own resources, but also to wake up the operation of subsequent teammates (this is the share).
3.4 releaseshared ()
The previous section has finished acquireshared (), and this section is about its anti-operation Releaseshared (). This method is a shared-mode thread that frees the shared resource from the top-level portal. It frees the specified amount of resources, and if it is completely freed (that is, state=0), it wakes up other threads in the wait queue to get the resources. Here is the source code for releaseshared ():
1 Public Final Boolean releaseshared (int arg) {2 if (tryreleaseshared (ARG)) {//try to free resource 3 doreleaseshared ();// Wake-up subsequent node 4 return true;5 }6 return false;7}
The process of this method is also relatively simple, in a word: After releasing the resources, wake up the successor. Similar to release () in exclusive mode, but it is a little bit to note that Tryrelease () in exclusive mode will not return true to wake other threads until the resource (state=0) is completely freed, mainly based on reentrant considerations , while Releaseshared () in shared mode has no such requirement, one is the essence of sharing-multithreading can be executed concurrently, and the other is that the shared mode is not re-entered (at least I haven't seen), so the custom Synchronizer can determine the return value as needed.
3.4.1 doreleaseshared ()
This method is mainly used for wake-up successors. The following is its source code:
1 private void Doreleaseshared () {2 for (;;) {3 Node h = head; 4 if (h! = NULL && h! = tail) {5 int ws = H.waitstatus; 6 if (ws = = node.signal) {7 if (!compare Andsetwaitstatus (H, node.signal, 0)) 8 continue; 9 unparksuccessor (h);//wake-up subsequent }11 else if (ws = = 0 & Amp;&12 !compareandsetwaitstatus (H, 0, node.propagate)) continue;14 }15 if (h = = head)// Head change break;17 }18}
3.5 Summary
In this section we explain the exclusive and shared two modes of access-release resources (Acquire-release, acquireshared-releaseshared) of the source, I believe we all have a certain understanding. It is worth noting that in both acquire () and acquiresahred (), threads are ignored in the wait queue. AQS also support the response interrupt, acquireinterruptibly ()/acquiresharedinterruptibly () that is, the corresponding source code here with acquire () and acquiresahred () almost, This is no longer a detailed explanation.
Four, simple application
Through the preceding chapters of the study, I believe that we have a basic understanding of the principle of Aqs. Here is a copy of the paragraph in the "Frames" section:
Different custom synchronizers compete for shared resources in different ways. The custom Synchronizer only needs to realize the acquisition and release of the shared resource state when it is implemented, as long as the specific thread waits for the queue to be maintained (such as getting the resource failed to join/wake up the team, etc.), the AQS has been implemented at the top level. There are several main ways to implement a custom Synchronizer implementation:
- Isheldexclusively (): Whether the thread is monopolizing the resource. Only use the condition to realize it.
- Tryacquire (int): Exclusive mode. Attempts to get a resource, success returns TRUE, and failure returns false.
- Tryrelease (int): Exclusive mode. Attempt to free the resource, success returns TRUE, and failure returns false.
- tryacquireshared (int): How to share. Try to get the resource. A negative number indicates a failure; 0 indicates success, but there is no remaining available resources; A positive number indicates success and there are remaining resources.
- tryreleaseshared (int): How to share. Attempt to free the resource, success returns TRUE, and failure returns false.
OK, below we take AQS source of the mutex as an example, talk about the simple application of Aqs.
4.1 Mutex (mutex)
A mutex is a non-reentrant implementation of a mutex lock. The lock resource (state in Aqs) has only two states: 0 for unlocked, and 1 for locking. Below is the core source of the mutex:
1 class Mutex implements Lock, java.io.Serializable {2//Custom Synchronizer 3 private static class Sync extends Abstractque Uedsynchronizer {4//Determines whether the lock state 5 protected Boolean isheldexclusively () {6 return getState () = = 1; 7} 8 9//try to get the resource and return immediately. Success returns True, otherwise false. The public boolean tryacquire (int acquires) {One-to-one assert acquires = = 1;//This limit can only be 1 quantities if (c Ompareandsetstate (0, 1)) {//state 0 is set to 1 and cannot be re-entered! Setexclusiveownerthread (Thread.CurrentThread ());//set to current thread exclusive resource return true;15 }16 return false;17}18 19//Attempt to release resources and return immediately. The success is true, otherwise false. protected Boolean tryrelease (int releases) {Six assert releases = = 1;//limited to 1 volume if (GE Tstate () = = 0)//Since the release, it must have been in possession of the state. Just for insurance, multi-layered judgment! The new Illegalmonitorstateexception (); Setexclusiveownerthread (null); Tstate (0);//release of resources, abandonment of possessionStatus true;27 return}28}29 30//The implementation of the true synchronization class relies on a custom synchronizer that inherits from Aqs! Private final Sync sync = new sync ();//lock<-->acquire. The semantics are the same: get the resource, even if it waits until success returns. public void Lock () {sync.acquire (1);}37//trylock<-->tryacquire. The semantics are the same: try to get the resources and ask for immediate return. The success is true, and the failure is false. Trylock public Boolean () {Sync.tryacquire (1),}42//unlock<-->release. The same language: release resources. The public void unlock () {sync.release (1); 46}47 48//The lock is in possession of the status of the public boolean isLocked () {50 return sync.isheldexclusively (); 51}52}
When implemented, a synchronization class typically defines a custom Synchronizer (sync) as an internal class for its own use, while the synchronization class itself (the Mutex) implements an interface for external service. Of course, the implementation of the interface to rely directly on sync, they also have some kind of semantic correspondence relationship!! and sync only with the realization of the resource State acquisition-release mode tryacquire-tryrelelase, as for thread queuing, waiting, wake up, etc., the upper Aqs have been achieved, we do not care.
In addition to mutex,reentrantlock/countdownlatch/semphore these synchronization classes are implemented in much the same way that different places are tryacquire-tryrelelase in the way of acquiring-freeing resources. Mastering this, the core of AQS is breached!
OK, so far, the whole aqs of the explanation will be down the curtain. Hope this article can learn Java concurrent Programming students have some reference, the middle of the wrong place, also welcome discussion and correction ~
Java Concurrency Aqs detailed