Aqs's design idea is to provide a template through inheritance so that you can easily implement a personalized synchronizer based on different scenarios. The core of Synchronizer is to manage a shared state, which can implement different locking mechanisms through the control of State. AQS design must consider the complex duplication and error-prone queue management work Unified abstraction management, and to unify control of the process, and exposed to the subclass call method is mainly the operation of shared State method, in order to provide atomic operation of the state. The general subclass of the Synchronizer using AQS provided by the GetState, SetState, Compareandsetstate three methods, the first two is the normal get and set methods, to use these two methods must ensure that there is no data competition, The Compareandsetstate method provides a hardware-level atomic update of the CAS mode. For exclusive mode, the definition of the lock acquisition and release process is given to acquire and release two methods, which define the logic of lock acquisition and release, as well as the interface that is provided to the subclass to acquire and release the lock. Its execution logic can refer to the previous "lock acquisition and release", which provides a powerful template? The following pseudo-code can be clearly demonstrated, please note that the two methods of Tryacquire and Tryrelease, which is left to the subclasses to personalize the method, through the two methods of sharing state management can customize a variety of Synchronizer, The management of the queue and the control of the process is not a problem you need to consider.
① Lock get Template
if (Tryacquire (ARG)) {
Create node
Using CAs to insert node into the tail of the queue
while (true) {
if (Tryacquire (ARG) and node's predecessor is the head node) {
Set the current node as the head node
Jump out of the loop
}else{
Use CAs to modify node precursor nodes ' waitstatus identity as signal
if (modified successfully)
Suspend current thread
}
}
Ii Lock Release Template
if (Tryrelease (ARG)) {
Wake up the thread that the subsequent node contains
}
We can assume that the Synchronizer can implement any different lock semantics, the lock that is generally provided to the user is a higher level implementation implemented with the AQS framework package, and provides a more graphic API for users to use more convenient and concise, rather than let the user directly contact the AQS framework, for example, Reentrantlock, Semphore, Countdownlatch, and so on, these different image locks make it easier for you to use better understanding, and it's not easy to confuse. However, these locks are implemented by AQS, the Aqs synchronizer is oriented to the control of thread and state, defines the mechanism of thread acquiring state and thread queueing, and so on, it is very good to isolate the focus of the two, the high-level concern is the use of the scene, and the Aqs Synchronizer is concerned with concurrency control. If you want to implement a custom Synchronizer, it is officially recommended that the subclass of the integrated Aqs synchronizer be the inner class of the synchronization device, and that the associated operation in the synchronization device only needs to be represented by the corresponding method in the subclass. Down with a simple example to see how to implement your own lock, because the Synchronizer is divided into two modes, exclusive mode and sharing mode, so the example also corresponds to the given.
① Exclusive mode, the example of exclusive mode is the Bank service window, if a bank branch has only one service window, then this Bank service window can only serve one person at the same time, others must wait in line, so this kind of bank window Synchronizer is an exclusive model. The first class is the Bank window Synchronizer class, which follows the recommended practice of using a subclass of an inherited Aqs Synchronizer and appears as a subclass. The second class is the test class, the image point, there are three people to the bank to transact business, is Tom, Jim and Jay, we can use Bankservicewindow to bind them to line up, a round to deal with the business and avoid into chaos.
public class Bankservicewindow {
Private final sync sync;
Public Bankservicewindow () {
sync = new sync ();
}
private static class Sync extends Abstractqueuedsynchronizer {
public boolean tryacquire (int acquires) {
if (compareandsetstate (0, 1)) {
Setexclusiveownerthread (Thread.CurrentThread ());
return true;
}
return false;
}
protected Boolean tryrelease (int releases) {
if (getState () = = 0)
throw new Illegalmonitorstateexception ();
Setexclusiveownerthread (NULL);
SetState (0);
return true;
}
}
public void handle () {
Sync.acquire (1);
}
public void Unhandle () {
Sync.release (1);
}
}
public class Bankservicewindowtest {
public static void Main (string[] args) {
Final Bankservicewindow bankservicewindow=new Bankservicewindow ();
Thread Tom=new thread () {
public void Run () {
Bankservicewindow.handle ();
System.out.println ("Tom begins to transact business");
try {
This.sleep (5000);
} catch (Interruptedexception e) {
E.printstacktrace ();
}
System.out.println ("Tom ends The Business");
Bankservicewindow.unhandle ();
}
};
Thread Jim=new thread () {
public void Run () {
Bankservicewindow.handle ();
System.out.println ("Jim begins to transact business");
try {
This.sleep (5000);
} catch (Interruptedexception e) {
E.printstacktrace ();
}
System.out.println ("Jim ends The Business");
Bankservicewindow.unhandle ();
}
};
Thread Jay=new thread () {
public void Run () {
Bankservicewindow.handle ();
System.out.println ("Jay begins to transact business");
try {
This.sleep (5000);
} catch (Interruptedexception e) {
E.printstacktrace ();
}
System.out.println ("Jay ends the Business");
Bankservicewindow.unhandle ();
}
};
Tom.start ();
Jim.start ();
Jay.start ();
}
}
The output results are as follows:
Tom begins to transact business
Tom ends the business process
Jim begins to transact business
Jim ends the business process
Jay begins to transact business
Jay ends Processing business
Obviously Tom, Jim, Jay Three are queued to complete, but can not guarantee the order of the third, may be Tom, Jim, Jay, may be Tom, Jay, Jim, because in the into row before the execution is not certain, its semantics is to ensure one after another processing. If there is no Synchronizer limit, the output will not be predictable and may be output as follows:
Jim begins to transact business
Jay begins to transact business
Tom begins to transact business
Jay ends Processing business
Jim ends the business process
Tom ends the business process
Ii Shared mode, Shared mode example is also the Bank Service window, with the development of this dot, more and more people handle business, a service window has been unable to meet the demand, so another staff opened another service window, then can serve two people at the same time, But all two windows are occupied and must also be queued, and the service window Synchronizer device is a shared type. The first class is a shared-mode synchronization appliance class, which differs from exclusive mode in that the initial value of its state can be freely defined, and that getting and releasing is the decrement and summation of the state. The second class is the Test class, where Tom, Jim, and Jay come to the bank again, and a two-window is very happy, and they can handle it at the same time, with two people.
public class Bankservicewindows {
Private final sync sync;
public bankservicewindows (int count) {
sync = new sync (count);
}
private static class Sync extends Abstractqueuedsynchronizer {
Sync (int count) {
SetState (count);
}
public int tryacquireshared (int interval) {
for (;;) {
int current = GetState ();
int newcount = current-1;
if (Newcount < 0 | | compareandsetstate (current, newcount)) {
return newcount;
}
}
}
public boolean tryreleaseshared (int interval) {
for (;;) {
int current = GetState ();
int Newcount = current + 1;
if (Compareandsetstate (current, newcount)) {
return true;
}
}
}
}
public void handle () {
Sync.acquireshared (1);
}
public void Unhandle () {
Sync.releaseshared (1);
}
}
public class Bankservicewindowstest {
public static void Main (string[] args) {
Final Bankservicewindows bankservicewindows=new bankservicewindows (2);
Thread Tom=new thread () {
public void Run () {
Bankservicewindows.handle ();
System.out.println ("Tom begins to transact business");
try {
This.sleep (5000);
} catch (Interruptedexception e) {
E.printstacktrace ();
}
System.out.println ("Tom ends The Business");
Bankservicewindows.unhandle ();
}
};
Thread Jim=new thread () {
public void Run () {
Bankservicewindows.handle ();
System.out.println ("Jim begins to transact business");
try {
This.sleep (5000);
} catch (Interruptedexception e) {
E.printstacktrace ();
}
System.out.println ("Jim ends The Business");
Bankservicewindows.unhandle ();
}
};
Thread Jay=new thread () {
public void Run () {
Bankservicewindows.handle ();
System.out.println ("Jay begins to transact business");
try {
This.sleep (5000);
} catch (Interruptedexception e) {
E.printstacktrace ();
}
System.out.println ("Jay ends the Business");
Bankservicewindows.unhandle ();
}
};
Tom.start ();
Jim.start ();
Jay.start ();
}
}
The possible output results are:
Tom begins to transact business
Jay begins to transact business
Jay ends Processing business
Tom ends the business process
Jim begins to transact business
Jim ends the business process
Tom and Jay started the business almost at the same time, and Jim came in after Jay ended up with a free service window.
This section is mainly about how to use Aqs to build your own synchronizer, and to analyze the logic of the lock acquisition and release template, so that you better understand the implementation of AQS, and finally give an example of the implementation of the exclusive mode and shared mode Synchronizer, I believe you understand the implementation of these two ways, To build a more complex synchronizer, you know where the force is going to go.
Java concurrency framework--aqs How to build a synchronizer using Aqs