Reprint Please specify source: http://www.cnblogs.com/skywang12345/p/3505809.html
Readwritelock and Reentrantreadwritelock introduction
Readwritelock, as the name implies, is a read-write lock. It maintains a pair of related locks--"read lock" and "write lock", one for the read operation and the other for the write operation.
Read locks are used for read-only operations, which are "shared locks" and can be fetched by multiple threads at the same time.
Write locks are used for write operations, which are exclusive locks, and write locks can only be obtained by a single thread lock.
Note: Read and write locks cannot exist at the same time!
Readwritelock is an interface. Reentrantreadwritelock is its implementation class, Reentrantreadwritelock includes subclasses Readlock and Writelock.
List of Readwritelock and Reentrantreadwritelock functions
List of Readwritelock functions
Returns the lock used for the read operation. Lock Readlock ()//returns the lock used for the write operation. Lock Writelock ()
List of Reentrantreadwritelock functions
Create a new Reentrantreadwritelock, by default, with an "unfair policy." Reentrantreadwritelock ()//Create a new Reentrantreadwritelock,fair is a "fair strategy". Fair is true, meaning a fair strategy; otherwise, it means a non-fair strategy. Reentrantreadwritelock (Boolean Fair)//returns the thread that currently owns the write lock, or null if no such thread is present. Protected thread GetOwner ()//Returns a collection that contains a thread that may be waiting to acquire a read lock. Protected collection<thread> getqueuedreaderthreads ()//Returns a Collection that contains a thread that may be waiting to acquire a read or write lock. Protected collection<thread> getqueuedthreads ()//Returns a Collection that contains a thread that may be waiting to acquire a write lock. Protected collection<thread> getqueuedwriterthreads ()//Returns the estimated number of threads waiting to get read or write locks. int getqueuelength ()//Query the number of Reentrant read locks held by the current thread in this lock. int Getreadholdcount ()//query the number of read locks held for this lock. int Getreadlockcount ()//Returns a collection that contains those threads that may be waiting for a given condition related to the write lock. Protected collection<thread> getwaitingthreads (Condition Condition)//Returns the number of thread estimates that are waiting for a given condition related to write locks. int Getwaitqueuelength (Condition Condition)//Query the number of reentrant write locks held by the current thread in this lock. int Getwriteholdcount ()//query whether a given thread is waiting to acquire a read or write lock. Boolean hasqueuedthread (thread thread)//query whether all threads are waiting to acquire read or write locks. Boolean HasqueuedthreaDS ()//query whether some threads are waiting for a given condition related to write locks. Boolean haswaiters (Condition Condition)//Returns TRUE if this lock sets the fairness to ture. Boolean Isfair ()//query whether a thread holds a write lock. Boolean iswritelocked ()//queries whether the current thread holds a write lock. Boolean iswritelockedbycurrentthread ()//returns the lock used for the read operation. Reentrantreadwritelock.readlock Readlock ()//returns the lock used for the write operation. Reentrantreadwritelock.writelock Writelock ()
REENTRANTREADWRITELOCK Data Structure
The UML class diagram for Reentrantreadwritelock is as follows:
It can be seen from:
Reentrantreadwritelock implements the Readwritelock interface. The Readwritelock is a read-write lock interface that provides a read-lock Readlock () function and a writelock () function to get a write lock.
Reentrantreadwritelock includes: Sync object, read lock ReaderLock, and write lock WriterLock. Both the read lock Readlock and the write lock Writelock implement the lock interface. Read lock Readlock and write lock Writelock also contain "Sync Object", their sync object and Reentrantreadwritelock Sync object is the same, that is, through sync, read lock and write lock to achieve the same object access.
(03) Like "Reentrantlock", Sync is the sync type, and sync is an abstract class that inherits from Aqs. Sync also includes "fair lock" Fairsync and "unfair lock" nonfairsync. The Sync object is one of "Fairsync" and "Nonfairsync", and the default is "Nonfairsync".
Reference code (based on jdk1.7.0_40)
Reentrantreadwritelock's Complete source code
View Code
Aqs's Complete source code
View Code
Among them, the shared lock source code related codes are as follows:
public static class Readlock implements Lock, java.io.Serializable {private static final long Serialversionuid =-5992 448646407690164L; Reentrantreadwritelock's Aqs object private final sync sync; Protected Readlock (Reentrantreadwritelock lock) {sync = Lock.sync; }//get "shared lock" public void Lock () {sync.acquireshared (1); }//If the thread is in a break state, throw a field or try to acquire a shared lock. public void lockinterruptibly () throws Interruptedexception {sync.acquiresharedinterruptibly (1); }//try to get "shared lock" public boolean Trylock () {return sync.tryreadlock (); }//For the specified time, try to get the "shared lock" public boolean trylock (long timeout, timeunit unit) throws Interruptedexception { Return Sync.tryacquiresharednanos (1, Unit.tonanos (timeout)); }//release "shared lock" public void unlock () {sync.releaseshared (1); }//New condition Public Condition newcondition () {throw new unsupportedoperationexception (); } public String toString () {iNT R = Sync.getreadlockcount (); return super.tostring () + "[Read locks =" + R + "]"; }}
Description :
Sync in Readlock is a Sync object, and sync inherits from the Aqs class, where sync is a lock. There is also a sync object in the Reentrantreadwritelock, and sync in Readlock is the correspondence between sync and Reentrantreadwritelock. That is, Reentrantreadwritelock and Readlock share the same Aqs object and share the same lock.
The definition of sync in Reentrantreadwritelock is as follows:
Final sync sync;
Below, the shared lock is described in two ways: "Get shared lock" and "release shared lock" respectively.
get a shared lock
The idea of acquiring a shared lock (the step of the lock function) is to attempt to acquire a shared lock by tryacquireshared (). If the attempt succeeds, it is returned directly; if the attempt fails, the doacquireshared () loops through the loop and attempts to acquire the lock and, if necessary, blocks the wait. Doacquireshared () attempts to acquire a lock in the loop every time it is attempted, through Tryacquireshared (). Here's a look at the detailed process for "get shared lock".
1. Lock ()
Lock () in Readlock, the source code is as follows:
public void Lock () { sync.acquireshared (1);}
2. acquireshared ()
Sync inherits from the aqs,acquireshared () definition in Aqs. The source code is as follows:
Public final void acquireshared (int arg) { if (tryacquireshared (ARG) < 0) doacquireshared (ARG);}
description : acquireshared () first attempts to acquire a lock through tryacquireshared ().
If the attempt succeeds, no action is taken (because the lock has been successfully acquired).
If the attempt fails, the lock is acquired through Doacquireshared (). Doacquireshared () will get to the lock before returning.
3. tryacquireshared ()
Tryacquireshared () defined in the Reentrantreadwritelock.java Sync, the source code is as follows:
Protected final int tryacquireshared (int unused) {Thread current = Thread.CurrentThread (); Gets the state of the "lock" int c = getState (); If the lock is a mutex and the thread that acquires the lock is not the current thread, then 1 is returned. if (Exclusivecount (c)! = 0 && getexclusiveownerthread ()! = current) return-1; Gets the shared count of "read lock" int r = Sharedcount (c); If no blocking wait is required, and the share count for read lock is less than max_count,//the "State of lock" is updated through the CAS function, the shared count of read locks is +1. if (!readershouldblock () && R < Max_count && Compareandsetstate (c, C + shared_unit)) { The 1th time gets the read lock. if (r = = 0) {Firstreader = current; Firstreaderholdcount = 1; If the thread that wants to get the lock (current) is the 1th thread that acquires the lock (Firstreader)} else if (Firstreader = = current) {Firstreaderholdcount ++; } else {//Holdcounter is used to count the number of times that the thread acquires a "read lock." Holdcounter RH = Cachedholdcounter; if (RH = = NULL | | Rh.tid! = current.getid ()) Cachedholdcounter = RH = Readholds.get (); else if (Rh.count = = 0) readholds.set (RH); The number of times that the thread gets the "read lock" +1. rh.count++; } return 1; } return fulltryacquireshared (current);}
Description : The Role of tryacquireshared () is to attempt to acquire a "shared lock."
If you try to acquire a lock, "no blocking Waits" and "share count of read locks is less than max_count", the share count of read locks is updated directly through the CAS function and the current thread gets the number of read locks +1.
Otherwise, the read lock is obtained by fulltryacquireshared ().
4. fulltryacquireshared ()
Fulltryacquireshared () defined in Reentrantreadwritelock, the source code is as follows:
Final int fulltryacquireshared (Thread current) {holdcounter RH = null; for (;;) {//Get the state of "lock" int c = getState (); If the lock is a mutex and the thread that acquires the lock is not the current thread, then 1 is returned. if (Exclusivecount (c)! = 0) {if (Getexclusiveownerthread ()! = current) return-1; If you want to block the wait. (01) When the "Blocking Wait" thread is the 1th thread that acquires a lock, it continues to execute. (02) Returns 1 if the number of threads that "need to block wait" acquires a lock = 0 o'clock. } else if (Readershouldblock ()) {//If the thread that wants to acquire the lock (current) is the 1th thread that acquires the lock (Firstreader) if (Firstreader = = C urrent) {} else {if (RH = = NULL) {RH = Cachedholdcounter; if (RH = = NULL | | Rh.tid! = Current.getid ()) {RH = Readholds.get (); if (Rh.count = = 0) readholds.remove (); }}//returns-1 if the current thread acquires a count of locks = 0. if (Rh.count = = 0) return-1; }}//If "No blocking wait" is required, the shared statistics for read lock are obtained, and if the share statistics exceed max_count, an exception is thrown. if (Sharedcount (c) = = Max_count) throw new Error ("Maximum lock COUNT exceeded"); The number of times that the thread gets the "read lock" +1. if (Compareandsetstate (c, C + shared_unit)) {//If it is the 1th time to get "read lock", update Firstreader and Firstreaderholdcount. if (Sharedcount (c) = = 0) {Firstreader = current; Firstreaderholdcount = 1; If the thread that wants to acquire the lock (current) is the 1th thread that acquires the lock (Firstreader),//Will firstreaderholdcount+1. } else if (Firstreader = = current) {firstreaderholdcount++; } else {if (RH = = NULL) RH = Cachedholdcounter; if (RH = = NULL | | Rh.tid! = current.getid ()) RH = Readholds.get (); else if (Rh.count = = 0) readholds.set (RH); The update thread gets a shared count of "read lock" rh.count++; CachedholdcounteR = RH; Cache for release} return 1; } }}
description : fulltryacquireshared () is processed based on whether blocking waits is required, whether the shared count of read locks exceeds the limit, and so on. If no blocking waits are required, and the share count for the lock does not exceed the limit, the CAS tries to acquire the lock and returns 1.
5. Doacquireshared ()
Doacquireshared () is defined in the AQS function, the source code is as follows:
The function of private void doacquireshared (int arg) {//Addwaiter (node.shared) is to create the node corresponding to the current thread and add the thread to the CLH queue. Final node node = addwaiter (node.shared); Boolean failed = true; try {Boolean interrupted = false; for (;;) {//Get the previous node of "node" final nodes P = node.predecessor (); If the current thread is the header of the CLH queue, try to acquire a shared lock. 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 "current thread" is not the header of the CLH queue, the Shouldparkafterfailedacquire () determines if it needs to wait,//if required, by PARKANDCH Eckinterrupt () to block the wait. Set interrupted to True if the thread has been interrupted while blocking the wait process. if (Shouldparkafterfailedacquire (p, node) && parkandcheckinterrupt ()) interrupted = true; }} finally {if (failed) cancelacquire (node); }}
Description : The Role of doacquireshared () is to acquire a shared lock.
It first creates the node for the CLH queue that corresponds to the thread, and then adds that node to the CLH queue. The CLH queue is the queue that manages the waiting thread that acquires the lock.
If "Current thread" is the header of the CLH queue, try to acquire a shared lock, otherwise you will need to determine whether to block the wait by Shouldparkafterfailedacquire () or, if necessary, through Parkandcheckinterrupt () To block the wait.
Doacquireshared () will continue to perform the above operations through a for loop, with the goal of acquiring a shared lock. It is important to note that doacquireshared () is executed by tryacquireshared () at every attempt to acquire a lock.
Shouldparkafterfailedacquire (), Parkandcheckinterrupt () and other functions have been in the "Java Multithreading Series-" Juc lock "03 of the Fair Lock (a)" in detail, here will not repeat the explanation.
releasing a shared lock
The idea of releasing a shared lock is to attempt to release a shared lock by tryreleaseshared (). If the attempt succeeds, the "other thread waiting to acquire a shared lock" is awakened by doreleaseshared () and returns true; otherwise, the flase is returned.
1. Unlock ()
public void Unlock () { sync.releaseshared (1);}
Description : The function actually calls releaseshared (1) to release the shared lock.
2. releaseshared ()
Releaseshared () implemented in Aqs, the source code is as follows:
Public final Boolean releaseshared (int arg) { if (tryreleaseshared (ARG)) { doreleaseshared (); return true; } return false;}
Description : The purpose of releaseshared () is to have the current thread release the shared lock it holds.
It will first attempt to release the shared lock through tryreleaseshared (). If the attempt succeeds, it returns directly; if the attempt fails, the shared lock is released through Doreleaseshared ().
3. tryreleaseshared ()
Tryreleaseshared () defined in the Reentrantreadwritelock, the source code is as follows:
Protected Final Boolean tryreleaseshared (int unused) {//Gets the current thread, which is the thread that freed the shared lock. Thread current = Thread.CurrentThread (); If the thread that wants to release the lock (current) is the 1th thread that acquires the lock (Firstreader),//And the "1th thread acquires the lock" = 1, the set firstreader is null;//Otherwise, the number of fetches for the "1th thread that acquires the lock "-1. if (Firstreader = = current) {//Assert firstreaderholdcount > 0; if (Firstreaderholdcount = = 1) Firstreader = null; else firstreaderholdcount--; Gets the RH object and updates the "current thread acquires lock information". } else {Holdcounter RH = Cachedholdcounter; if (RH = = NULL | | Rh.tid! = current.getid ()) RH = Readholds.get (); int count = Rh.count; if (count <= 1) {readholds.remove (); if (count <= 0) throw unmatchedunlockexception (); }--rh.count; } for (;;) {//Get the state of the lock int c = GetState (); Number of locks to be acquired-1. int NEXTC = C-shared_unit; Updates the state of the lock through CAs. if (Compareandsetstate (c, NEXTC)) return NEXTC = = 0; }}
Description : The Role of tryreleaseshared () is to attempt to release a shared lock.
4. doreleaseshared ()
Doreleaseshared () defined in the Aqs, the source code is as follows:
private void doreleaseshared () {for (;;) { //Get the head node of the CLH queue nodes h = head; If the head node is not NULL, and the head node is not equal to the tail node. if (h! = NULL && h! = tail) { //Gets the state of the thread corresponding to the head node int ws = H.waitstatus; If the thread corresponding to the head node is the signal state, it means that the thread corresponding to the next node of the head node needs to be awakened by Unpark. if (ws = = node.signal) { //sets the thread state corresponding to the head node is empty. If it fails, the loop continues. if (!compareandsetwaitstatus (H, node.signal, 0)) continue; Wakes the thread corresponding to the next node of the head node. unparksuccessor (h); } If the thread corresponding to the head node is empty, set the shared lock owned by the corresponding thread of the file point to get the empty state of the lock for the other thread. else if (ws = = 0 && !compareandsetwaitstatus (H, 0, node.propagate)) continue; Loop on failed CAS } //If the head node changes, the loop continues. Otherwise, exit the loop. if (h = = head) //loop If head changed break ; }}
description : doreleaseshared () releases "shared lock". It will traverse the CLH queue from the go, "Wake up" and then "execute" the corresponding thread for each node in the queue, and the ultimate goal is to let these threads release the locks they hold.
Fair share lock and non-fair shared lock
Like the mutex Reentrantlock, the Readlock is also divided into fair and non-fair locks.
The difference between a fair lock and a non-fair lock is reflected in whether the function Readershouldblock () that requires blocking is different.
The source code for the Fair Lock Readershouldblock () is as follows:
Final Boolean readershouldblock () { return hasqueuedpredecessors ();}
In a fair share lock, returns True if there are other threads waiting in front of the current thread to acquire a shared lock, otherwise, false is returned.
The source code for the Readershouldblock () is not fair lock as follows:
Final Boolean readershouldblock () { return apparentlyfirstqueuedisexclusive ();}
In an unfair shared lock, it ignores whether any other threads in front of the current thread are waiting to acquire a shared lock. Returns true as long as the non-fair shared lock corresponds to a thread that is not NULL.
Reentrantreadwritelock Example
1 Import Java.util.concurrent.locks.ReadWriteLock; 2 Import Java.util.concurrent.locks.ReentrantReadWriteLock; 3 4 public class ReadWriteLockTest1 {5 6 public static void main (string[] args) {7//Create account 8 MyC Ount MyCount = new MyCount ("4238920615242830", 10000); 9//Create a user, and specify the account with user user = new User ("Tommy", MyCount); 11 12//Start 3 "Read account Money" thread and 3 "Set account Money" thread for (int i=0; i<3; i++) {user.getcash (); 15 User.setcash ((i+1) *1000);}17} (), class User {+ private String name; User name: Private MyCount MyCount; The account to be operated on is Readwritelock MyLock; The lock object required to perform the operation (String name, MyCount MyCount) {this.name = name; this.mycount = MyCount; This.mylock = new Reentrantreadwritelock ();}30 public void GetCash () {new Thread () {3 3 public void Run () {myLock.readlock (). Lock (); try {System.out.println (Thread.CurrentThread (). GetName () + "getcash start"); 37 Mycount.getcash (), Thread.Sleep (1), System.out.println (Thr Ead.currentthread (). GetName () + "GetCash End"); (Interruptedexception e) {$} finally {Mylock.readlock ( ). Unlock (); }44}45}.start ();}47, public void Setcash (final int cash) {49 New Thread () {public void run () {Mylock.writelock (). Lock (); System.out.println (Thread.CurrentThread (). GetName () + "setcash start"); Mycount.setcash (Cash), Thread.Sleep (1); System.out.prin TLN (Thread.CurrentThread (). GetName () + "Setcash End"); Interrupt} catch (Edexception e) {()} finally {Mylock.writelock (). Unlock (); 60}61 }62}.start ();}64}65-Class MyCount {in-the-private String ID; Account number: private int cash; Account balance MyCount (String ID, int cash) {this.id = id; this.cash = cash; Ublic string GetId () {return id; setId (string id) {this.id = ID; 81 } getcash public int () {System.out.println (Thread.CurrentThread (). GetName () + "GetCash cash=" + Cash); Cash return; Setcash (int cash) {System.out.println (Thread.CurrentThread ()). GetName () + SETCA SH cash= "+ cash); This.cash = cash; 91} 92}
Operation result :
Thread-0 getcash startThread-2 getcash startThread-0 getcash cash=10000thread-2 getcash cash=10000thread-0 GetCash EndThread-2 getcash endThread-1 setcash startThread-1 setcash cash=1000thread-1 setcash endThread-3 Setcash StartThread-3 setcash cash=2000thread-3 setcash endThread-4 getcash startThread-4 getcash cash=2000thread-4 GetCash EndThread-5 setcash startThread-5 setcash cash=3000thread-5 Setcash End
Result Description :
(01) Observing the results of the operation of Thread0 and Thread-2, we found that the Thread-0 started and acquired a "read lock", and when it had not finished running, Thread-2 also started and successfully acquired a "read lock".
Therefore, "read lock" support is obtained simultaneously by multiple threads.
(02) Observe the thread that thread-1,thread-3,thread-5 these three "write locks". As long as the write lock is fetched by a thread, the thread has finished running before releasing the lock.
Therefore, write locks are not supported for simultaneous acquisition by multiple threads.
Java lock--shared lock and Reentrantreadwritelock