Java-concurrency icon indicating copy to clipboard operation
Java-concurrency copied to clipboard

关于AbstractQueuedSynchronizer类中enq方法并没有让tail指向新的尾节点的两个疑问

Open MikasaLee opened this issue 6 years ago • 1 comments

private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
			if (t == null) { // Must initialize
				//1. 构造头结点
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
				// 2. 尾插入,CAS操作失败自旋尝试
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
}

以上为enq()方法。 当队列为null时进入的enq方法,那 第一次循环是创建了头结点并且head,tail都指向此头结点。 那第二次循环是将参数node加入该头结点之后,形成第一个节点。但是为啥代码中tail没有更新呢?(意思就是现在tail,head,t都是指向"头结点")

自己的猜测:是否是在compareAndSetTail(t, node)中更新了tail值。

第二个问题是为什么返回t而不是tail?毕竟不管上面的假设成立不成立,t都指向新插入节点的前一个节点,而不是新节点。

MikasaLee avatar May 16 '18 13:05 MikasaLee

第一个问题

compareAndSetTail代码如下: private final boolean compareAndSetTail(Node expect, Node update) { return unsafe.compareAndSwapObject(this, tailOffset, expect, update); } tailOffset就是tail域在AQS对象里的偏移量,用CAS更新尾结点。第一个不用猜测,点进去看看就知道了

第二个问题

从设计上我实在讲不出什么,只能说返回tail没什么意义,可以从以下几点理解

  1. enq(node)可以简单地理解为插入吧?CAS操作成功后,node即tail,你传了一个node,返回一个一样的node有什么意义呢?
  2. 看transferForSignal(node),它的作用是唤醒node结点,根据AQS的规定,需要将前一个结点,即node.prev设置成SIGNAL状态。这部分看Condition部分

:) 我也是最近才开始看并发的内容,感觉能有人一起讨论问题还是挺好的,你可以加我QQ:624684849 ,一起讨论问题

CoDeleven avatar May 19 '18 09:05 CoDeleven