以下是使用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,同时保持代码等效)
答案 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的示例费用:
此特定技术的参考:
[鲁道夫& Segall 84]鲁道夫,L。和 Segall,Z。Dy-namic分散 缓存方案forMIMD Parallel 处理器。 Invùroceedingsof theíúvúth年度研讨会 计算机体系结构,页面 340i> 347,1984。
答案 2 :(得分:0)
它是非阻塞链表算法。 详细说明见JCP书(15.4.2。非阻塞链表)