ReentrantReadWriteLock之读锁释放锁流程
流程概述:
- 判断当前线程是否是第一个持有读锁的线程
- 如果是,判断当前线程的重入次数
- 如果重入次数是1,将firstReader设置为null
- 如果重入次数大于1,将firstReaderHoldCount减1
- 如果当前线程不是第一个持有读锁的线程,判断当前线程是否是最后一个持有读锁的线程
- 如果不是,从当前线程的ThreadLocal中获取锁重入信息,如果是,从cachedHoldCounter获取锁重入信息
- 如果重入次数<=1,删除ThreadLocal中的信息
- 如果重入次数<=0,说明释放多了,需要抛异常
- 如果重入次数>1,减少当前线程的锁重入信息-1
- 锁重入信息修改成功后,将state高16位值-1
- 如果state高16位值-1后,state值为0,执行doReleaseShared方法
- 获取head节点,判断head节点状态是否为-1,如果是说明有挂起的节点
- 如果有挂起的节点,将head的状态改为0,然后唤醒挂起的节点
public void unlock() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
// 释放读锁
// 修改state值
// 修改锁重入信息
if (tryReleaseShared(arg)) {
// 释放读锁,唤醒同步队列中的节点
doReleaseShared();
return true;
}
return false;
}
protected final boolean tryReleaseShared(int unused) {
// 获取当前线程
Thread current = Thread.currentThread();
if (firstReader == current) {
// 当前线程是第一个持有读锁的
if (firstReaderHoldCount == 1)
// 当前线程的重入次数为1
firstReader = null;
else
// 当前线程的重入次数大于1
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
// 当前线程不是最后一个持有读锁的线程
// 读锁重入信息需要从当前线程的ThreadLocal中获取
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
// 说明需要释放干净,避免ThreadLocal内存泄漏
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count;
}
for (;;) {
int c = getState();
// 修改state高16位的值
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
// 如果nextc == 0说明已经没有持有锁的线程,则可以去唤醒AQS中的线程
return nextc == 0;
}
}
private void doReleaseShared() {
for (;;) {
// 获取head节点
Node h = head;
if (h != null && h != tail) {
// 说明有挂起的线程
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
// 判断是否成功将head的状态改成0
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
// 状态修改失败
continue;
// 唤醒head后面一个节点
unparkSuccessor(h);
}
// compareAndSetWaitStatus(h, 0, Node.PROPAGATE)与信号量有关
// 与读写锁无关,不用关心
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
if (h == head)
break;
}
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
// 节点状态是-1,改成0
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
// 唤醒下一个节点
LockSupport.unpark(s.thread);
}