接上篇 AQS源码解析(上)
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return;
node.thread = null;
// Skip cancelled predecessors
/**
* 此处和shouldParkAfterFailedAcquire中的ws>0的情况类似
* 从当前节点往前遍历找到一个非取消状态的节点;
* 注意此时当前节点的状态依然是合法状态还没被置为CANCELLED
* 如果在下面操作之前就改为CANCELLED的话,那么就会和
* shouldParkAfterFailedAcquire中的ws>0的情况有并发安全的问题;
* 所以在找到一个合法节点以后才会改为CANCELLED,以防并发问题。
*/
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
//获取到当前节点pre节点的原next节点并保存以便于后面的CAS操作
Node predNext = pred.next;
// Can use unconditional write instead of CAS here.
// After this atomic step, other Nodes can skip past us.
// Before, we are free of interference from other threads.
node.waitStatus = Node.CANCELLED;//此处才改为CANCELLED
// If we are the tail, remove ourselves.
/**
* 如果当前节点是tail,那么cas替换tail
* 如果成功的话,cas替换原predNext,这点考虑很充分,不愧大神
* 因为有可能替换tail后,已经有waiter加入了,所以必须用cas
*/
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.
/**
* 下面的逻辑是考虑怎么把pred和node.next连接起来
* 1.如果新pred不是头节点
* 2.ws等于SIGNAL或者原来<=0并且cas成功变为SIGNAL,
* 主要想把pred.ws设置为SIGNAL,以便于后续作为唤醒线程的标志。
* 这里还要和共享模式下的doReleaseShared联系一下,因为此刻很可能
* 已经传播到这里,其他线程刚把ws改为0并unpark,而这里又把ws改为SIGNAL,
* 所以再次印证,一个线程可能会被多次unpark,不过不影响。
* 3.pred.thread != null,再次判断pred是否已经唤醒并获取到资源
* 上面三种情况都满足的情况下,进行设置pred.next=取消节点.next
*/
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
//检查此刻的next是否已经被取消
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
//如果pred已经是头节点
unparkSuccessor(node);
}
node.next = node; // help GC
}
}
/**
* 把条件队列的节点移动到同步队列中去进而排队等待资源
*/
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
//这个enq方法返回的是加入到同步队列的pre节点
Node p = enq(node);
int ws = p.waitStatus;
//如果此时节点已经取消或者节点的状态发生变化(比如传播或者唤醒)
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
/**
* 检测当前节点是否first节点
* h != t 表明队列至少两个节点或者是两个节点的路上
* h.next = null 在上一判断的基础上表明队列还没来得及给next赋值
* s.thread != Thread.currentThread()表明first node不是自己
*/
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}