In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article will explain in detail how to parse the Java multithreaded read-write lock ReentrantReadWriteLock class. The content of the article is of high quality, so the editor shares it for you as a reference. I hope you will have a certain understanding of the relevant knowledge after reading this article.
In real multithreaded business development, the most commonly used logic is data reading and writing. Although ReentrantLock has the effect of complete mutual exclusion (that is, only one thread is performing the task behind lock at the same time), although this ensures the thread safety of instance variables, it is very inefficient. Therefore, a read-write lock ReentrantReadWriteLock class is provided in JDK, which can be used to speed up the operation efficiency.
Read-write locks represent two locks, one is a read-related lock, called a shared lock, and the other is a write-related lock, called an exclusive lock.
Let's use the code to verify the mutual exclusion between read-write locks.
ReentrantReadWriteLock read sharing
First create an object and define a read lock method and a write lock method, respectively.
Public class MyDomain3 {private ReentrantReadWriteLock lock = new ReentrantReadWriteLock (); public void testReadLock () {try {lock.readLock (). Lock (); System.out.println (System.currentTimeMillis () + "acquire read Lock"); Thread.sleep (1000);} catch (InterruptedException e) {e.printStackTrace () } finally {lock.readLock (). Unlock ();}} public void testWriteLock () {try {lock.writeLock (). Lock (); System.out.println (System.currentTimeMillis () + "acquire write lock"); Thread.sleep (1000);} catch (InterruptedException e) {e.printStackTrace () } finally {lock.writeLock () .unlock ();}}
Create thread class 1 to call the read lock method
Public class Mythread3_1 extends Thread {private MyDomain3 myDomain3; public Mythread3_1 (MyDomain3 myDomain3) {this.myDomain3 = myDomain3;} @ Override public void run () {myDomain3.testReadLock ();}} @ Test public void test3 () throws InterruptedException {MyDomain3 myDomain3 = new MyDomain3 (); Mythread3_1 readLock = new Mythread3_1 (myDomain3); Mythread3_1 readLock2 = new Mythread3_1 (myDomain3); readLock.start () ReadLock2.start (); Thread.sleep (3000);}
Execution result:
1639621812838 acquire read lock 1639621812839 acquire read lock
You can see that the two read locks are executed almost at the same time, indicating that reads and reads are shared because there are no thread safety problems with read operations.
Write mutually exclusive
Create thread class 2 and call the write lock method
Public class Mythread3_2 extends Thread {private MyDomain3 myDomain3; public Mythread3_2 (MyDomain3 myDomain3) {this.myDomain3 = myDomain3;} @ Override public void run () {myDomain3.testWriteLock ();}} @ Test public void test3 () throws InterruptedException {MyDomain3 myDomain3 = new MyDomain3 (); Mythread3_2 writeLock = new Mythread3_2 (myDomain3); Mythread3_2 writeLock2 = new Mythread3_2 (myDomain3) WriteLock.start (); writeLock2.start (); Thread.sleep (3000);}
Execution result:
1639622063226 acquire write lock 1639622064226 acquire write lock
In terms of time, the interval is 1000ms, or 1s, indicating that the write lock and the write lock are mutually exclusive.
Read and write mutually exclusive
Then use thread 1 and thread 2 to call read lock and write lock respectively.
@ Test public void test3 () throws InterruptedException {MyDomain3 myDomain3 = new MyDomain3 (); Mythread3_1 readLock = new Mythread3_1 (myDomain3); Mythread3_2 writeLock = new Mythread3_2 (myDomain3); readLock.start (); writeLock.start (); Thread.sleep (3000);}
Execution result:
1639622338402 acquire read lock 1639622339402 acquire write lock
In terms of time, the interval is 1000ms or 1s, which is consistent with the code, which proves that reading and writing are mutually exclusive.
Note that "read and write mutually exclusive" and "write and read mutually exclusive" are two different scenarios, but the proof and conclusion are the same, so they don't prove it.
Under the final test results:
1. There is no mutual exclusion between read and read, because there will be no thread safety problems in read operations.
2. Mutual exclusion between write and write to prevent one write from affecting another and causing thread safety problems.
3. Mutual exclusion between read and write to prevent the write operation from modifying the content during the read operation, causing thread safety problems.
To sum up, multiple Thread can read at the same time, but only one Thread is allowed to write at a time.
Source code analysis
Sync in the read-write lock also implements AQS, recalling the implementation of the custom synchronizer in ReentrantLock. The synchronization state represents the number of times the lock is repeatedly acquired by a thread, while the custom synchronizer of the read-write lock needs to maintain the state of multiple readers and one writer thread on the synchronization state (an integer variable), which makes the design of this state the key to the implementation of the read-write lock.
The read-write lock cuts the variable into two parts, with the high 16 bits for reading and the low 16 bits for writing
The current synchronization state indicates that a thread has acquired the write lock, re-entered it twice, and acquired the read lock twice in a row. How do read-write locks quickly determine the status of read and write respectively?
Static final int SHARED_SHIFT = 16 static final int SHARED_UNIT = (1 SHARED_SHIFT;} / * * Returns the number of exclusive holds represented in count * / static int exclusiveCount (int c) {return c & EXCLUSIVE_MASK;}
It's actually through bit operations. Suppose the current synchronization state is c, the write state is equal to c & EXCLUSIVE_MASK (c&0x0000FFFF (erase all the high 16 bits), and the read state is c > 16 (unsigned complement 0 is shifted 16 bits to the right). When the write state increases by 1, it equals to clocked 1, and when the read state increases by 1, it equals to c + (1 > 16) greater than 0, that is, the read lock has been acquired.
Acquisition and release of write lock
Through the above test, we know that the write lock is an exclusive lock that supports reentrant. Take a look at how the source code realizes the acquisition of the write lock.
Protected final boolean tryAcquire (int acquires) {/ * * Walkthrough: * 1. If read count nonzero or write count nonzero * and owner is a different thread, fail. 2. If count would saturate, fail. (This can only * happen if count is already nonzero.) 3. Otherwise, this thread is eligible for lock if * it is either a reentrant acquire or * queue policy allows it. If so, update state * and set owner. * / Thread current = Thread.currentThread (); int c = getState (); int w = exclusiveCount (c); if (c! = 0) {/ / (Note: if c! = 0 and w = = 0 then shared count! = 0) if (w = 0 | current! = getExclusiveOwnerThread ()) return false If (w + exclusiveCount (acquires) > MAX_COUNT) throw new Error ("Maximum lock count exceeded"); / / Reentrant acquire setState (c + acquires); return true;} if (writerShouldBlock () | |! compareAndSetState (c, c + acquires)) return false SetExclusiveOwnerThread (current); return true;}
Lines 3 to 11 briefly describe the implementation logic of the entire method. To boast here, this comment makes it easy for people to know the functionality of the code. Let's analyze it from lines 13 to 15. We get the lock state value c of the current thread object current,lock and the write lock value w of the write lock. 0 indicates that we are currently in a locked state, and then continue to analyze lines 16 to 25. There is a key Note: (Note: if clocking 0 and w = = 0 then shared count! = 0): to put it simply: if a thread is locked but does not have a write lock, it must have a read lock.
Line 18 if condition, which determines that a read lock is added, but the current thread is not the thread owned by the lock, then the acquisition of the lock fails, which proves that the read-write lock is mutually exclusive.
Lines 20 to 25, at this point, indicate that w! = 0, the write lock has been acquired, as long as the maximum value of the write lock is not exceeded, then increase the write state and then you can successfully acquire the write lock.
If the code goes to line 26, it shows that clocked 0 is not currently added, so first execute the writerShouldBlock () method, which is used to determine whether the write lock should be blocked. This block has different logic for fair and unfair locks. For unfair locks, return false directly without blocking. The following is the judgment of fair lock execution.
Public final boolean hasQueuedPredecessors () {/ / The correctness of this depends on head being initialized / / before tail and on head.next being accurate if the current / / thread is first in queue. Node t = tail; / / Read fields in reverse initialization order Node h = head; Node s; return h! = t & & (s = h.next) = = null | | s.thread! = Thread.currentThread ();}
For fair locks, it is necessary to determine whether there are threads in the current waiting queue that are equal to the current thread and are queuing to acquire the lock.
The release process of the write lock is basically similar to that of the ReentrantLock. Each release reduces the write state. When the write state is 0, the write lock has been released, so that the waiting read-write thread can continue to access the read-write lock, and the modification of the previous write thread is visible to the subsequent read-write thread.
Acquisition and release of read lock
A read lock is a shared lock that supports reentry and can be acquired by multiple threads at the same time. The JDK source code is as follows:
Protected final int tryAcquireShared (int unused) {Thread current = Thread.currentThread (); int c = getState (); if (exclusiveCount (c)! = 0 & & getExclusiveOwnerThread ()! = current) return-1; int r = sharedCount (c) If (! readerShouldBlock () & & r < MAX_COUNT & & compareAndSetState (c, c + SHARED_UNIT)) {if (r = = 0) {firstReader = current; firstReaderHoldCount = 1;} else if (firstReader = = current) {firstReaderHoldCount++ } else {HoldCounter rh = cachedHoldCounter; if (rh = = null | | rh.tid! = getThreadId (current)) cachedHoldCounter = rh = readHolds.get (); else if (rh.count = = 0) readHolds.set (rh); rh.count++ } return 1;} return fullTryAcquireShared (current);}
Lines 4 to 6, if the write lock is held by another thread, returns false directly and fails to acquire the read lock, proving that the write-read mutual exclusion between different threads is proved.
Line 8, readerShouldBlock () gets whether the read lock should be blocked, and here we also need to distinguish between fair lock and unfair lock. The fair lock mode needs to determine whether there is a thread in the current waiting queue that is equal to the current thread and is queuing to acquire the lock.
The unfair lock mode needs to determine that the first one in the current waiting queue is waiting for the write lock, then the method returns true, and the read lock needs to wait.
FullTryAcquireShared () mainly deals with the full version of read lock acquisition, dealing with CAS errors that are not handled in tryAcquireShared () and the processing logic of reentrant read locks.
On how to parse the Java multithreaded read-write lock ReentrantReadWriteLock class is shared here, I hope the above content can be of some help to you, can learn more knowledge. If you think the article is good, you can share it for more people to see.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.