前面一篇文章写了如何获取锁:多线程系列学习:AQS(一)获取锁,下面以ReentrantLock的unlock()方法为入口,看看AQS如何释放锁:
public void unlock() {
sync.release(1);
}
接着看release()方法:
public final boolean release(int arg) {
// 如果释放锁成功
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); // 唤醒下一个继任者
return true;
}
return false;
}
主要分析这两个方法做了什么:
- tryRelease()
- unparkSuccessor()
1. tryRelease
protected final boolean tryRelease(int releases) {
// 同步状态-1
int c = getState() - releases;
// 检查释放有释放锁的权限:如果当前线程没有持有锁,则抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果同步状态减到了0,则表示成功释放了锁,其他线程可以获取锁了
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
// 更新同步状态
setState(c);
return free;
}
可以看到,没调用一次tryRelease()方法,同步状态就减1,直到为0,这也说了,ReentrantLock是可重入锁。
2. unparkSuccessor
private void unparkSuccessor(Node node) {
// 当前节点的状态
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
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); // 唤醒节点s
}
有一点,不理解的是,为什么要从后往前遍历呢?