非阻塞链表 一行代码不理解

nowto
  • 199

使用cas操作实现非阻塞链表 其中put方法 代码如下

public class LinkedQueue <E> {
    private static class Node <E> {
        final E item;
        final AtomicReference<Node<E>> next;
        Node(E item, Node<E> next) {
            this.item = item;
            this.next = new AtomicReference<Node<E>>(next);
        }
    }
    private AtomicReference<Node<E>> head
        = new AtomicReference<Node<E>>(new Node<E>(null, null));
    private AtomicReference<Node<E>> tail = head;
    public boolean put(E item) {
        Node<E> newNode = new Node<E>(item, null);
        while (true) {
            Node<E> curTail = tail.get();//1
            Node<E> residue = curTail.next.get();
            if (curTail == tail.get()) {//2
                if (residue == null) /* A */ {
                    if (curTail.next.compareAndSet(null, newNode)) /* C */ {
                        tail.compareAndSet(curTail, newNode) /* D */ ;
                        return true;
                    }
                } else {
                    tail.compareAndSet(curTail, residue) /* B */;
                }
            }
        }
    }
}

代码来源: developerworks文章《非阻塞算法简介》 非阻塞链表 章节

疑惑:这段代码的大体思路、原理 是理解的,但2处注释的代码,即if (curTail == tail.get())这行的判断不理解。这个判断是有必要的吗?可以去掉吗?为什么?

2处的curTail就是1处tail.get() 的结果。如果需要判断的话,应该是在并发情况下,1、2处分别调用tail.get()的返回的可能不是同一个对象。也就是说,2处判断的时候,tail可能被修改了。1到2处之间没有修改tail的动作,所以只能是别的一个线程修改了tail;只看这段代码的话,只能是别的线程调用put方法时执行了B或D。所以安插了这么一个判断。

但是有什么用呢?这是个“先判断后执行”的动作,但是因为非阻塞不加锁,[判断+执行]并没有被同步起来;所以,可能刚刚判断之后,tail就被改掉,判断就会失效。所以看起来这个判断是完全可以去掉的。是为了性能,判断失败后 可以少走一次循环体吗?

回复
阅读 2.1k
1 个回答
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏