上篇文章主要介绍了ReentrantLock的lock
方法,这次来看看unlock
的底层是如何实现的。
release
如前面所说,release是AQS的模板方法,tryRelease由子类实现。tryRelease成功后,便会调用unparkSuccessor
唤醒后继节点。h.waitStatus != 0
对于ReentrantLock来说就是waitStatus为-1,也就是SIGNAL的情况,如果一个节点的waitStatus为-1,它就有责任唤醒后继节点。
此方法执行完后
//unlock调用AQS的release
public void unlock() {
this.sync.release(1);
}
//AQS的release方法
public final boolean release(int arg) {
if (this.tryRelease(arg)) {
AbstractQueuedSynchronizer.Node h = this.head;
if (h != null && h.waitStatus != 0) {
//唤醒后继节点
this.unparkSuccessor(h);
}
return true;
} else {
return false;
}
}
unparkSuccessor
此方法的作用是唤醒节点中park的线程。需要注意的是,我们前面说过CHL队列的头节点是dummyHead,作用是标记,里面不放线程信息。所以此方法传入的形参节点是真正被唤醒节点的上一个waitStatus为-1的队列头节点。
private void unparkSuccessor(AbstractQueuedSynchronizer.Node node) {
int ws = node.waitStatus;
if (ws < 0) {//将node的waitStatus设为0,即正常状态
node.compareAndSetWaitStatus(ws, 0);
}
//获取node的下一个节点
AbstractQueuedSynchronizer.Node s = node.next;
//如果s是CANCEL状态(1),则从队列尾部向前找到可唤醒状态的节点
if (s == null || s.waitStatus > 0) {
s = null;
for(AbstractQueuedSynchronizer.Node p = this.tail; p != node && p != null; p = p.prev) {
if (p.waitStatus <= 0) {
s = p;
}
}
}
//unparks节点里的线程
if (s != null) {
LockSupport.unpark(s.thread);
}
}
tryRelease
protected final boolean tryRelease(int releases) {
//解锁的逻辑就是state-1
int c = this.getState() - releases;
//互斥锁,如果当前持锁的不是自己直接抛异常了
if (Thread.currentThread() != this.getExclusiveOwnerThread()) {
throw new IllegalMonitorStateException();
} else {
boolean free = false;
if (c == 0) {//state如果解锁之后状态为0,则释放锁,否则不释放锁
free = true;
this.setExclusiveOwnerThread((Thread)null);
}
//锁重入情况,此时依旧持有锁,只不过state--
this.setState(c);
//释放失败,自己依旧持有锁
return free;
}
}