二十七、ReentrantReadWriteLock之读锁释放锁流程

64 阅读2分钟

ReentrantReadWriteLock之读锁释放锁流程

流程概述:

  1. 判断当前线程是否是第一个持有读锁的线程
  2. 如果是,判断当前线程的重入次数
  3. 如果重入次数是1,将firstReader设置为null
  4. 如果重入次数大于1,将firstReaderHoldCount减1
  5. 如果当前线程不是第一个持有读锁的线程,判断当前线程是否是最后一个持有读锁的线程
  6. 如果不是,从当前线程的ThreadLocal中获取锁重入信息,如果是,从cachedHoldCounter获取锁重入信息
  7. 如果重入次数<=1,删除ThreadLocal中的信息
  8. 如果重入次数<=0,说明释放多了,需要抛异常
  9. 如果重入次数>1,减少当前线程的锁重入信息-1
  10. 锁重入信息修改成功后,将state高16位值-1
  11. 如果state高16位值-1后,state值为0,执行doReleaseShared方法
  12. 获取head节点,判断head节点状态是否为-1,如果是说明有挂起的节点
  13. 如果有挂起的节点,将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);
}