private Node enq(final Node node) {
for (;;) {
Node t = tail;
//如果队列为空则新建头结点
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))//这个操作是新建队列
//将tail指向头结点
tail = head;
} else {
//1.将新节点的前置指针指向链表尾部
node.prev = t;
//2.通过CAS将tail的引用指向node
//这里的设计很厉害,总体来说先tail 指向本节点 ,如果其他线程进来也不影响
//t局部变量是为了暂时保存前节点,compareAndSetTail(t,node) 这个操作只是做了tail 指向新节点;如//果本县城做完了compareAndSetTail(t,node) 此时有个线程进来了,并且要操作队列,那他直接操作
tail就可以了,等本新线程继续执行了,他继续操作本线程的前节点指向本线程的当前节点。
if (compareAndSetTail(t, node)) {//只是让tail 指向了 新节点
//3.将原尾部节点的后置指针指向新节点
t.next = node;//所以这里前节点需要指向后节点
//for循环的出口
return t;
}
}
}
}
这是节点进入同步队列的方法,其中的CAS操作compareAndSetTail(t, node),作用为将tail的引用指向node,参数t中的内容是tail的旧引用对象地址,node是新节点的地址。再来看看AQS中compareAndSetTail(t, node)具体内容,
-
private final boolean compareAndSetTail(Node expect, Node update) {
-
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
-
}
-
tailOffset = unsafe.objectFieldOffset
-
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
这里的tailOffset,是通过unsafe类中的方法获取到的是“tail”变量相对于Java对象的“起始地址”的偏移量,即tailOffset可以理解为tail的地址。也就是说这里CAS操作更新的知识tail变量的地址上的数据,并不会对局部变量t上的值做修改,执行完compareAndSetTail(t, node)后,t还是指向原来的旧链尾,而tail指向了新的节点。