代码如下:
代码语言:javascript复制private Node addWaiter(Node mode) {
// 当前线程构造成Node节点
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
// 尝试快速在尾节点后新增节点 提升算法效率 先将尾节点指向pred
Node pred = tail;
if (pred != null) {
//尾节点不为空 当前线程节点的前驱节点指向尾节点
node.prev = pred;
//并发处理 尾节点有可能已经不是之前的节点 所以需要CAS更新
if (compareAndSetTail(pred, node)) {
//CAS更新成功 当前线程为尾节点 原先尾节点的后续节点就是当前节点
pred.next = node;
return node;
}
}
//第一个入队的节点或者是尾节点后续节点新增失败时进入enq
enq(node);
return node;
}
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;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
节点进入同步队列之后,就进入了一个自旋的过程,每个线程节点都在自省地观察,当条件满足,获取到了同步状态,就可以从这个自旋过程中退出,否则依旧留在这个自旋过程中并会阻塞节点的线程,代码如下:
代码语言:javascript复制final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取当前线程节点的前驱节点
final Node p = node.predecessor();
//前驱节点为头节点且成功获取同步状态
if (p == head && tryAcquire(arg)) {
//设置当前节点为头节点
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//是否阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}