1. ReentrantReadWriteLock profile
ReentrantReadWriteLock is suitable for handling the situation where there are too many reads and too few writes. The synchronous state can be held by multiple reader threads if no other writer thread holds the lock. If the current synchronization state is held by a write lock, all other threads are blocked.
2. ReentrantReadWriteLock implementation
The read-write lock also synchronizes with a custom synchronizer, and the read-write state is the synchronization state of its synchronizer. ReentrantLock uses state to identify the number of reentrants of a lock. ReentrantReadWriteLock also needs to represent the number of reentrants of a read lock and the number of reentrants of a write lock, but only has one state variable. Read/write locks are reentrant counts of read locks by 16 bits higher and write locks by 16 bits lower.
If S is not 0 and the write status (S&0x0000FFFF) is 0, the read status (S>>>16) is greater than 0, that is, the read lock has been obtained.
2.1 Obtaining and releasing write locks
A write lock is an exclusive lock that supports reentrant.
- Write lock acquisition
The write lock is acquired by overriding the tryAcquire method in AQS. If the read lock is already occupied by another thread, the write lock fails to be acquired. Otherwise, the lock is successfully acquired by reentrant.
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if(c ! =0) {
// If the read lock has already been acquired by the read thread or the current thread is not already acquired by the write lock, the synchronization status fails to be acquired
if (w == 0|| current ! = getExclusiveOwnerThread())return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// The reentrant count increases
setState(c + acquires);
return true;
}
If c=0 and neither read lock nor write lock is occupied, check whether the write lock should be blocked
if(writerShouldBlock() || ! compareAndSetState(c, c + acquires))return false;
setExclusiveOwnerThread(current);
return true;
}
Copy the code
– Write lock release
protected final boolean tryRelease(int releases) {
if(! isHeldExclusively())throw new IllegalMonitorStateException();
// Synchronization state minus write state
int nextc = getState() - releases;
// Determine whether the write status of the current thread is 0, if so, release the write lock
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
Copy the code
2.2 Obtaining and releasing read locks
- Read lock acquisition
If another thread acquires the write lock, the current thread fails to acquire the read lock and enters the wait state. If the current thread acquires a write lock or fails to acquire a write lock, the current thread adds the read state and successfully acquires the lock.
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
// If the write lock has already been acquired and is not in the current thread, return it directly
// If the program obtains the write lock first, it can obtain the read lock later, which is called lock degradation
if(exclusiveCount(c) ! =0&& getExclusiveOwnerThread() ! = current)return -1;
int r = sharedCount(c);
// The current thread acquires the lock (the reader thread should not block, the number of read locks is less than the maximum, CAS is successful)
if(! readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) {// If the state of the read lock is 0, another thread has acquired the lock
if (r == 0) {
// set the current thread to be the first thread to acquire the lock
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
// The current thread is the first thread to acquire the lock, reentrant
firstReaderHoldCount++;
} else {
// The state of the read lock is not 0 and the current thread is not firstReader
// The last thread counter that successfully acquired the read lock
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);
}
Copy the code
- Read lock release
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null|| rh.tid ! = getThreadId(current)) rh = readHolds.get();int count = rh.count;
if (count <= 1) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count;
}
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
return nextc == 0; }}Copy the code