Hello everyone, I am a xing, today is a core article, please fasten your seat belts, the train will start soon.
Carsick friends, can first eat a star exclusive secret carsick medicine, fair trade, genuine, but also free, white piao party ecstasy (16 pictures opened AQS).
The outline of this paper is as follows
Throughout the global
My English name is ReentrantReadWriteLock (RRW for short). People like to call me “read and write lock” because I spend a lot of time reading and writing.
Read/write lock specification
To be a qualified read/write lock, read and write locks are required.
Therefore, the ReadWriteLock interface is declared as the basic specification for reading and writing locks.
After all around the specification to achieve read lock and write lock.
Read locks and write locks
WriteLock and ReadLock are read and write locks, and they are products of RRW’s ReadWriteLock interface.
However, read and write locks should also follow the basic rules of lock operation.
So both WriteLock and ReadLock implement the Lock interface.
How is the WriteLock/readLock-to-lock interface implemented?
Of course, it’s our old friend AQS.
AQS
As we all know, to achieve the basic operation of the lock, must rely on AQS big Brother.
AQS (AbstractQueuedSynchronizer) an abstract class defines a set of the synchronization of multithreaded access to a Shared resource template, solved the realization of synchronizer involved when a large number of details, can greatly reduce the implementation work, use old vernacular, AQS for locking and unlocking process provides a unified template function, Only a few details are left to the subclasses themselves.
AQS simplified flow chart is as follows
If readers want to dig into the details of AQS, they can check out This article by A Xing: 16 Images uncovering AQS
Sync
AQS provides a unified template function for the locking and unlocking process, with minor details left up to subclasses, but WriteLock and ReadLock do not inherit AQS directly.
Because WriteLock and ReadLock felt they had to inherit from AQS and implement abstract functions that they could share, it was cumbersome and repetitive.
Instead, we provide a separate class for locking, which is held by WriteLock and ReadLock, called Sync.
Sync inherits AQS to implement the following core abstraction functions
- tryAcquire
- release
- tryAcquireShared
- tryReleaseShared
TryAcquire and Release are for WriteLock write locks.
TryAcquireShared and tryReleaseShared are intended for ReadLock.
As mentioned above, Sync implements some of the core abstraction functions of AQS, but Sync itself also has some important content. Take a look at the following code
As we all know, A state variable is maintained in AQS. Normally, two variables are needed to maintain the read and write lock states. However, in order to save resources, high and low bits are used to achieve the state variable to maintain two states, namely, the high 16 bits represent the read state and the low 16 bits represent the write state.
For more details on how to design read/write lock states, see ReentrantReadWriteLock
Sync also defines a HoldCounter and a ThreadLocalHoldCounter
- A HoldCounter is an object used to record the number of read lock reentrant counts
- ThreadLocalHoldCounter is a ThreadLocal variable that holds a read-lock reentrant object for any thread other than the first thread to acquire a read-lock
If you’re not familiar with ThreadLocal, check out A Xing’s nanny-level tutorial on ThreadLocal in 22 images
Fair and unfair strategies
You see, ReentrantLock has fair and unfair policies, so ReentrantReadWriteLock has to have too.
What are fair and unfair strategies?
Because in the AQS process, threads that fail to acquire locks will be constructed as nodes to join the CLH queue, and other threads releasing locks will wake up threads in the CLH queue to re-compete for locks, as shown in the figure below (simplified process).
The unfair strategy is that a thread that’s not in the CLH queue is competing with a thread that’s in the CLH queue for a lock, and you’re not going to give it to you just because you’re in the CLH queue and you’re waiting in line for a long time.
The fair strategy means that the CLH queue thread will compete successfully if the lock is obtained in strict accordance with the ORDER of THE CLH queue. If the non-CLH queue thread keeps occupying the time slice, it will continue to fail until the time slice turns to the CLH queue thread, so the performance of the fair strategy will be worse.
To support fair and unfair policies, Sync extends the FairSync and NonfairSync subclasses. Both subclasses implement readerShouldBlock and writerShouldBlock functions, namely, whether the read lock and write lock block.
About readerShouldBlock, writerShouldBlock function where to use a star will be said later.
ReentrantReadWriteLock global figure
Finally, A Xing assembled all the previous content to form the following picture.
After having global view, the back can go into detail one by one to break.
Further details
After that, we only need to break five details, namely create read lock, acquire write lock, release write lock, acquire read lock, release read lock.
The creation of ReentrantReadWriteLock
The creation of a read/write lock initializes a set of classes, as shown below
ReentrantReadWriteLock is an unfair policy by default. If you want to use a fair policy, you can call the parameter constructor and pass true.
But creating either FairSync or NonfairSync triggers the no-parameter constructor of Sync because Sync is their parent class (essentially both of them are Sync).
Because Sync needs to be used by ReadLock and WriteLock, the ReentrantReadWriteLock object is received as an input parameter when you create ReadLock and WriteLock.
Finally through ReentrantReadWriteLock. Sync sync to ReadLock with WriteLock.
Access to write lock
We observe the ReadWriteLock interface specification, call ReentrantReadWriteLock. WriteLock write lock object function.
After obtaining the write Lock object, follow the Lock interface specification and call the Lock function to obtain the write Lock.
The writelock. lock function is implemented by Sync (FairSync or NonfairSync).
The sync.acquire(1) function is an exclusive lock acquisition process template in AQS (sync inherits from AQS).
The writelock. lock call chain is shown below
We only focus on the tryAcquire function. Other functions are contents of the process after AQS fails to acquire an exclusive lock, which do not belong to the scope of this paper. The code of tryAcquire function is as follows
To make it easier to understand, Xing turned it into a flow chart
Through the flow chart, we found some key points
- Read and write the mutex
- Write about a mutex
- Write locks support reentrant of the same thread
- WriterShouldBlock Whether a write lock blocks implementation depends onfair and unfair strategies (FairSync and NonfairSync)
Release the write lock
After obtaining the write Lock, release the write Lock (the number of times to release the write Lock if the reentry is repeated). Otherwise, read and write operations of other threads will be blocked. Call unlock to release the write Lock (Lock interface specification).
The writelock. unlock function is also implemented by Sync (FairSync or NonfairSync).
Sync.release (1) executes the exclusive lock release process template in AQS (sync inherits from AQS).
The call chain of writelock. unlock is shown in the following figure
Let’s take a look at the tryRelease function. The other functions are the contents of the AQS process after the successful release exclusive. they are outside the scope of this article
To make it easier to understand, Xing turned it into a flow chart
Since the same thread can re-enter the same write lock multiple times, the same number of times must be released.
Access to read lock
We observe the ReadWriteLock interface specification, call ReentrantReadWriteLock readLock function for read lock object.
After obtaining the read Lock object, follow the Lock interface specification and call the Lock function to obtain the read Lock.
The readLock. lock function is implemented by Sync (FairSync or NonfairSync).
The sync.Acquireshared (1) function executes the shared lock acquisition process template in AQS (sync inherits from AQS).
The readLock. lock invocation chain is shown below
We will only focus on the tryAcquireShared function, doAcquireShared is AQS after the process of obtaining the shared lock failed, do not belong to the scope of this article, tryAcquireShared function code is as follows
The code is quite a lot, for easy to understand, XING turned it into a flow chart
Through the flow chart, we found some key points
- Read lock share, read not mutually exclusive
- Read locks are reentrant, and each thread that acquires a read lock records the corresponding number of reentrants
- Reads and writes are mutually exclusive, except in lock degradation scenarios
- Support lock degradation. The thread holding the write lock can acquire the read lock, but remember to release the read lock and the write lock
- ReaderShouldBlock Whether the read lock blocks implementation depends onfair and unfair strategies (FairSync and NonfairSync)
Release read lock
After the critical section is executed, release the read Lock (the number of times required for multiple reentrings). Otherwise, write operations of other threads will be blocked. Call UNLOCK to release the read Lock (Lock interface specification).
The readlock. unlock function is also implemented by Sync (FairSync or NonfairSync).
The sync.releaseshared (1) function executes the shared lock release process template in AQS (sync inherits from AQS).
The readlock. unlock call chain is shown below
We only focus on the tryReleaseShared function. The doReleaseShared function is the content of AQS shared lock release process and is not included in this article. The tryReleaseShared function code is as follows
To make it easier to understand, Xing turned it into a flow chart
There are three points to note here
-
First point: the reentrant number of thread read locks and the number of read locks are two different concepts. The reentrant number of thread read locks is the number of times that each thread acquires the same read lock, and the number of read locks is the sum of the reentrant number of all threads.
-
Second point: in AQS shared lock release process template, doReleaseShared is not executed until all read locks have been released
-
Third point: Since the AQS shared process template is used, if the thread nodes behind the CLH queue are all read lock thread nodes blocked by write locks, wake up will be propagated
summary
ReentrantReadWriteLock is the same as ReentrantLock. Both of them are dependent on AQS. They declare a Sync that inherits AQS and extend fair and unfair policies under Sync. Subsequent lock-related operations are delegated to fair and unfair policies.
We also find that there are also shared templates in AQS in addition to exclusive templates. They are different in the process of accessing shared resources in multiple threads. For example, In ReentrantReadWriteLock, read locks are shared and write locks are exclusive.
Finally, the logic of write and read locks is reviewed
- Read without blocking
- The write lock blocks the read/write lock after the write, but does not block the read lock thread before the write lock
- Write locks are blocked by read/write locks prior to writing
- Wake up of the read lock node is propagated unconditionally to wake up the read lock node behind the CLH queue
- Write locks can be downgraded to read locks to prevent updates from being lost
- Both read and write locks support reentrant
Good historical articles recommended
- Thorough Java thread state transitions
- Talk about ReentrantReadWriteLock’s bit operations
- Easy to understand ReentrantLock, don’t understand you to chop me
- Word long | 16 picture AbstractQueuedSynchronizer
- LockSupport for small white look
- 13 pictures, in-depth understanding of Synchronized
- From shallow to deep CAS, Xiao Bai can also align with BAT interviewers
- Small white also can understand the Java memory model
- Nanny Level teaching, 22 images uncover ThreadLocal
- Can process, thread and coroutine be confused? A penny for you to know!
- What is thread safety? This article will give you insight
About me
A star is a Java program ape who loves technology. The public account “program ape A Star” regularly shares interesting and interesting original articles!
Thank you very much for your little brothers and sisters to see here, the original is not easy, the article can be helpful to pay attention to, point a like, share and comment, are support (don’t want white whoring)!
May you and I both go where we want to go. See you in the next article.