前言
前面两篇文章Java并发编程之ReentrantLock源码(一)和Java并发编程之ReentrantLock源码(二)介绍了ReentrantLock
加锁成功和失败的流程,这篇文章主要介绍释放锁的流程。
释放锁流程
尝试释放锁
// ReentrantLock类
public void unlock() {
sync.release(1);
}
// AQS类
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
解锁调用的是unlock
方法,内部调用了AQS
的release
方法,这里主要分为两部分来介绍:if
条件语句和if
语句块内部逻辑。
首先看下tryRelease
方法源码:
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;
}
首先获取state
的值然后减1
得到c
,如果当前线程不是锁的拥有者那就没有资格来进行解锁,所以就直接抛出了异常;如果c
为0
,表示释放了锁,把线程拥有者设为null
,然后修改state
的值直接返回true
;如果c
不等于0
,表示有重入的情况,所以没有完全释放锁,只是减少了一次重入,然后修改state
的值返回false
。
唤醒后继节点
回到上面release
方法,如果解锁失败就直接返回false
结束操作;如果解锁成功了,head
节点如果不为null
而且waitStatus
不等于0
,表示可以唤醒后继节点,唤醒方法unparkSuccessor
源码如下:
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 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);
}
这里传入的参数是head
节点,首先获取head
节点的waitStatus
,如果小于0
的话就尝试修改成0
,因为唤醒后继节点之后这个节点就没有用了。
中间部分的if
代码块的意思是:获取head
节点的后继节点准备唤醒它,但是如果这个节点是null
或者被取消了,那就需要继续往后找到一个合适的节点来唤醒,但是因为这个后继节点已经无效了,所以没办法通过它继续往后找,所以这里采用的是从尾部往前找的方法,最终找到一个离head
最近的节点。
最后判断如果这个节点不为null
,就唤醒这个节点的线程,唤醒之后这个线程就按照上一篇文章Java并发编程之ReentrantLock源码(二)中介绍的逻辑继续尝试获取锁,这时就会成功了。