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;
}
// 1.1 非公平锁 尝试释放锁
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false; // 释放是否锁
if (c == 0) { // 若状态=0,该线程已全部执行完
free = true;
setExclusiveOwnerThread(null);
}
setState(c); // 该线程仍然持有锁
return free;
}
//
private void unparkSuccessor(Node node) {
// 当前线程所在结点的状态
int ws = node.waitStatus;
if (ws < 0) // 如果状态<0。则将状态改为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);
}
当当前线程所在的节点的下个节点无效或者为空时,从尾节点开始向前找的原因:
- 线程A 执行完1,此时线程A所在的节点设置成了尾节点。此时发生线程切换
- 线程B 执行到1 也设置成了尾节点,且此时B的前驱节点为A。但A的next节点并没有指向B
- 此时再进行线程切换。线程C再执行unparkSuccessor 时,当前线程的next节点为A发现的next为空,但是此时A的下个节点是B。所以需要才从尾队列想前。
- 思考下🤔 如果把2 3 顺序兑换下 应该可以
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t; // 3
if (compareAndSetTail(t, node)) { // 1
t.next = node; // 2
return t;
}
}
}
}
总结:
- tryRelease 当前状态值-1,若-1后状态值=0,则返回true,表示可以释释放资源
- 找到当前节点下一个阻塞的线程。unpark()唤醒该线程。