读锁
tryAcquireShared()
加锁失败则返回-1 成功则返回>0
tryAcquireShared(arg) < 0
加锁
1. 在获取读锁时,会尝试判断当前对象是否拥有了写锁(排它),如果已经拥有,则直接失败。
2. 如果没有写锁,就表示当前对象没有排他锁,则当前线程会尝试给对象加锁
3. 如果当前线程已经持有了该对象的读锁,那么直接将读锁数量加1
protected final int tryAcquireShared(int unused) {
//这里主要是为了性能 缓存了第一次和最后一次加锁的信息
Thread current = Thread.currentThread();
int c = getState();
//首先判断是否被上了写锁
//exclusiveCount(c) != 0 标识上了写锁
//但是还会继续判断为什么上了写锁还要继续判断-----》重入或者降级
//然后再判断是否是重入如果这里是重入则一定是降级 如果不是重入则失败 读写需要互斥
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
//如果上面代码没有返回,执行到这里,有两种情况标识1、没有人上写锁 2、重入降级
int r = sharedCount(c);//得到r的上锁次数
if (!readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) {
//r 是加锁之前的
//r == 0 标识 这是第一次给这把锁加读锁 之前没有人加锁
if (r == 0) {
//如果是第一个线程第一次加锁(之前没有人加过锁),则把这个线程付给firstReader 局部变量
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);
}
加锁失败 阻塞
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
//被唤醒时再去拿锁
int r = tryAcquireShared(arg);
if (r >= 0) {
//如果队列中自己的后面也是读锁,把它也唤醒
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
//如果被唤醒,从这里开始
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}