ReentrantReadWriteLock分析
依然使用state变量表示锁的获取状态,因为读写锁涉及读锁和写锁,因此用state变量的高16位和低16位作为区分,分别代表读锁的获取次数,以及写锁的重入次数。
1、写锁加锁过程
protected final boolean tryAcquire(int acquires) {
// 当前获取锁的线程
Thread current = Thread.currentThread();
// 获取state的值,0代表没有任何锁
int c = getState();
// 获取写锁的值[低16位]
int w = exclusiveCount(c);
// 已经有锁了
if (c != 0) {
// 非写锁直接返回fasle, 是写锁但是非当前线程也返回false
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;
}
1.1 为什么w == 0直接返回fasle
因为c != 0 说明有线程已经获取到了锁,但是 w == 0 说明是读锁被获取,获取读锁的线程可能是当前线程,也可能是其他线程,也有可能是两者都是。
- 非当前线程:因为读写互斥,因此加锁失败
- 当前线程:也就是说,当前线程已经拿到了读锁,再来加写锁,考虑这样一种情况,在还未获取写锁成功的时候,有其他线程来获取读锁,因为读读并发,因此其他线程可以拿到读锁,然鹅当前线程再获取写锁,如果不返回false的话,则会造成,读锁和写锁同时被多个线程持有,打破了读写互斥的原则。
2、写锁释放过程
写锁释放过程相对简单,因为写锁是排他锁,因此和独占锁的释放过程是一样的。将写锁的状态值减去1,判断是否为0了,[考虑重入],为0唤醒下一个排队的线程。
3、读锁的加锁过程
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);
}