上一篇文章学习记录了ReentrantLock.lock()的实现原理,ReentrantLock.lock()
接下来继续学习ReentrantLock.unlock()的实现原理:
NonfairSync和FairSync都是直接调用的AQS的release(1)实现的
AQS的release()方法如下:
//java.util.concurrent.locks.AbstractQueuedSynchronizer#release
public final boolean release(int arg) {
if (tryRelease(arg)) {//①如果tryRealease()释放了锁
//acquireQueued操作完成后(拿到了锁),会将当前持有锁的节点设为头结点
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);//②唤醒头结点指向的下一个等待线程
return true;
}
return false;
}
①Sync对AQS中的tryRelease重写如下:
//java.util.concurrent.locks.ReentrantLock.Sync#tryRelease
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
//如果当前线程不是持有锁的线程
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//考虑可重入锁,如果c != 0,表示当前线程扔持有锁,其它线程继续阻塞
if (c == 0) {
free = true;
setExclusiveOwnerThread(null); //当前独占锁持有者置空
}
setState(c);
return free;
}
②AQS中unparkSuccessor实现如下:
//java.util.concurrent.locks.AbstractQueuedSynchronizer#unparkSuccessor
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)//清空头结点的waitStatus,也就是不再需要锁了
compareAndSetWaitStatus(node, ws, 0);
//获取下一个节点,如果下一个节点被取消或者为空,则从尾部开始查找,找到第一个可用的线程
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
//这里为什么要从tail遍历呢?③
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
③因为队列中的每个结点, 随时都可能自己取消自己, 一旦我们从前往后unpark万一遇到哪个线程突然把自己给取消了, next指针指向自己, 那么我们就没办法去继续向后查找满足条件的线程去unpark。
//java.util.concurrent.locks.AbstractQueuedSynchronizer#cancelAcquire
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return;
node.thread = null;
// Skip cancelled predecessors
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
Node predNext = pred.next;
node.waitStatus = Node.CANCELLED;
// If we are the tail, remove ourselves.
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
unparkSuccessor(node);
}
//③从前往后遍历出问题
node.next = node; // help GC
}
}