这些线路是否在无锁队列中是不必要的?

时间:2011-03-23 13:11:57

标签: java multithreading lock-free

以下是使用compareAndSet(在Java中)的无锁队列中的一些代码:

public void enq(T value) {
    Node newNode = new Node(value);
    while(true) {
        Node last = tail.get();
        Node next = last.next.get();

        if(last != tail.get())
            continue;   //???       

        if (next != null) { //improve tail
            tail.compareAndSet(last, next);
            continue;
        }

        if (last.next.compareAndSet(null, newNode)) {   //update last node
            tail.compareAndSet(last, newNode);  //update tail
            return;
        }
    }
}

public T deq() throws EmptyException {
    while(true) {
        Node first = head.get();
        Node last = tail.get();
        Node next = first.next.get();

        if(first != head.get())
            continue;   //???

        if(first == last) {
            if (next == null)
                throw new EmptyException();

            tail.compareAndSet(last, next);
            continue;
        }

        T value = next.value;
        if (head.compareAnsdSet(first, next)) {
            return value;
        }
    }
}

(头部和尾部是队列的成员)

在deq和enq函数中,第一次检查对我来说似乎没必要。 (评论为“???”的人) 我怀疑它只是用于某种优化。

我在这里遗漏了什么吗?这些检查会影响代码的正确性吗?

(代码取自“多处理器编程的艺术”,虽然我重构了代码样式以减少嵌套ifs和elses,同时保持代码等效)

3 个答案:

答案 0 :(得分:3)

是的,在Java中,鉴于它具有垃圾收集功能,那些ifs仅具有真正的优化价值,而且它有点大:CAS只是从内存中读取而非常昂贵,所以要确保值没有'在此期间改变了,从而降低了后续CAS失败的可能性,有助于减少CAS重试次数,从而有助于提高性能。

你也可以移动第一个== last&&尾部更新检查到head.CAS内部,作为进一步的优化:尾部只有在头部更新时才会滞后,因此只有在CAS成功时检查才有意义。你也可以在那里移动tail.get,因为你在其他任何地方都不需要它。示例代码如下。希望这有帮助!

public T deq() throws EmptyException {
while(true) {
    Node first = head.get();
    Node next = first.next.get();

    if (first != head.get())
        continue;

    if (next == null) {
        throw new EmptyException();
    }

    T value = next.value;

    if (head.compareAndSet(first, next)) {
        Node last = tail.get();

        if (first == last) {
            tail.compareAndSet(last, next);
        }

        return value;
    }
}

}

答案 1 :(得分:2)

它们不是必需的,但出于性能原因而使用,请注意在不使用原子操作的情况下进行检查。

来自MSDN的示例费用:

  • MemoryBarrier测量为20-90个周期。
  • InterlockedIncrement测量为36-90个循环。
  • 测量或释放临界区被测量为40-100个循环。
  • 测量或释放互斥量测量为大约750-2500次循环。

此特定技术的参考:

  

[鲁道夫& Segall 84]鲁道夫,L。和   Segall,Z。Dy-namic分散   缓存方案forMIMD Parallel   处理器。 Invùroceedingsof   theíúvúth年度研讨会   计算机体系结构,页面   340i> 347,1984。

答案 2 :(得分:0)

它是非阻塞链表算法。 详细说明见JCP书(15.4.2。非阻塞链表)