书接上文Java并发编程之ReentrantReadWriteLock(一),这篇继续介绍加锁的第三步
尝试加锁
// ReentrantReadWriteLock.Sync
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0) {// 有写锁 // 第一部分
if (getExclusiveOwnerThread() != current)
return -1;
} else if (readerShouldBlock()) {// 没有写锁,但是队列第二个是加写锁 // 第二部分
if (firstReader == current) {
} else {
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)
return -1;
}
}
if (sharedCount(c) == MAX_COUNT) // 第三部分
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) { // 第四部分
if (sharedCount(c) == 0) { // 没有获取过共享锁
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) { // 第一个获取共享锁的是当前线程
firstReaderHoldCount++;
} else { // 获取过共享锁并且第一个获取的线程不是当前线程
if (rh == null)
rh = cachedHoldCounter; // 先获取缓存的最后一个线程的持有锁数量
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get(); // 获取当前线程持有锁数量
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
这个方法主体部分是一个死循环,分为四部分分别进行介绍
第一部分
首先获取独占锁的数量,如果为0表示没有线程正在加写锁,就执行第二部分;不为0表示有线程加了写锁,再检查持有锁的线程是不是当前线程,如果不是就加锁失败,返回-1;否则表示当前线程加了写锁,就执行到第三部分去尝试获取读锁,这也就是锁降级的表现。
第二部分
-
检查队列中第二个节点是否是独占锁模式,不是的话进入第三部分尝试去获取锁;
-
是的话检查第一个获取读锁的线程是不是当前线程,如果是进入第三部分尝试去获取重入读锁;
-
否则进入
else部分,获取缓存的最后一次成功获取读锁的数量,- 如果是
null表示没有其他线程成功获取过读锁,或者最后一次获取成功读锁的线程不是当前线程,就获取当前线程持有锁数量; - 如果为
0表示当前线程没加过读锁,清除计数器然后返回-1表示加锁失败。
- 如果是
第三部分
如果共享锁数量达到最大值,抛出异常,否则进入第四部分。
第四部分
尝试加共享锁,如果失败了就重新回到for循环一直重试;加锁成功的话:
- 如果共享锁数量是
0,当前线程就是第一个加读锁的线程,修改firstReader、firstReaderHoldCount的值; - 如果共享锁数量不为
0但是第一个加锁的线程是当前线程,就直接在firstReaderHoldCount上加1; - 如果最后一个持有锁的线程不是当前线程,就更新当前线程持有锁数量,然后缓存到
cachedHoldCounter;如果最后一个线程是当前线程,就拿缓存的数量更新到线程本地readHolds,然后对count加1并更新缓存。