前面说了AQS,也说了ReentrantLock 加锁解锁的基本逻辑,加锁源码剖析,现在说解锁
解锁源码分析
先看图,加锁与解锁画在一起,直观感受下
public void unlock() {
sync.release(1);
}
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(int releases)
是修改state值的
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
这段代码比较容易理解,也不存在并发,
调用一次 unlock(),state 值减1,当state = 0,即解锁成功,清除线程。
也就是说,连续加锁三次,即调用了三次lock() 方法,state=3,
解锁也得是3次,否则锁不会被释放。
unparkSuccessor(Node node)
是唤醒节点的
if (h != null && h.waitStatus != 0) // h == null 无等待队列,h.waitStatus == 0,说明后面没有阻塞的队列,即不需要唤醒。
unparkSuccessor(h);
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); // 负值设置为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); // 将后继节点唤醒。
}
// 这段代码其实很有意思的,说点题外话
shouldParkAfterFailedAcquire() 这个方法还记得不,
要阻塞节点的前提是,该节点的前驱节点 waitStatus= -1
unparkSuccessor 这个方法中,s 是后继节点,若是null,说明不存在等待队列,无需唤醒。
for() 循环中代码,可保证,s 是最前面的那个排除节点,唤醒它抢锁,因为锁释放了。
上一篇: ReentrantLock 加锁源码剖析
下一篇: 公平锁与非公平锁的源码对比