【问题标题】:Concurrent linked queue use CAS并发链接队列使用 CAS
【发布时间】:2018-12-25 11:15:26
【问题描述】:

我在 IBM Developer 中找到了这个关于并发链接队列的代码段。

但我无法理解其中的一部分。这是

while(true){
    ...
    if(curTail == Tail.get()){
        if(residue == null){
            ...
        }
    }
}

根据curTail和residual的定义,我认为curTail是Tail的副本,curTail是等于Tail.next的指针。

我担心函数 compareAndSet 会判断调用者对象是否等于第一个参数,为什么必须在调用这个函数之前判断它们?我认为下面的代码可以做到同样的事情。

        while (true) {
            Node<E> curTail = tail.get();
            Node<E> residue = curTail.next.get();
                if (curTail.next.compareAndSet(null, newNode)) /* C */ {
                    tail.compareAndSet(curTail, newNode) /* D */ ;
                    return true;
                } else {
                    tail.compareAndSet(curTail, residue) /* B */;
                }
            }
        }

任何帮助将不胜感激。 谢谢。

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();
            Node<E> residue = curTail.next.get();
            if (curTail == tail.get()) {
                if (residue == null) /* A */ {
                    if (curTail.next.compareAndSet(null, newNode)) /* C */ {
                        tail.compareAndSet(curTail, newNode) /* D */ ;
                        return true;
                    }
                } else {
                    tail.compareAndSet(curTail, residue) /* B */;
                }
            }
        }
    }
}

【问题讨论】:

标签: java multithreading thread-safety


【解决方案1】:

供参考:例如,算法的代码解释可在this page from the IBM Developer website上找到。

上面链接的页面将为您提供每个操作 ABCD 的用途,以及为什么要求它们允许其中引用的内容作为建设性干扰在同时更新队列尾部的线程之间。

您的更改破坏了算法。当 C 不成功时,else 子句必须执行。它所扮演的角色是在一个线程从另一个线程截获未完成的 [*] 尾部更新时重现操作 D

[*]:即在C之后D之前。

要了解失败的原因和时间,请考虑以下场景。

while (true) {
   Node<E> curTail = tail.get();
   Node<E> residue = curTail.next.get();

   /* (i) */

   if (curTail.next.compareAndSet(null, newNode)) /* C */ {
     tail.compareAndSet(curTail, newNode) /* D */ ;
     return true;
   } else {
      tail.compareAndSet(curTail, residue) /* B */;
   }
}
  • T1T2 两个线程同时从位置 (i) 开始。他们的堆栈包含对curTailresidue 的相同引用。 residue 应该是 null(即队列应该处于静止状态)。

  • T1 成功完成第一个 CAS C。它还没有执行 D

  • T2 CAS 失败C,进入else,成功执行CAS B,因为引用tail没有改变。

  • T1 未能通过 CAS D,因为分配给 tail 的引用已由 C 设置为 null .没有回退,方法退出。 T2 中的元素未插入。

  • 第二次尝试T1,分配给tail 的引用是nullcurTail.next 抛出NullPointerException。数据结构已损坏。

总而言之,AB 成对工作。它们的存在是为了确保干扰线程可以帮助队列收敛到正常状态,并从处于中间状态的队列中恢复。想象一个线程执行 C,但在有机会运行 D 之前被杀死。如果没有 AB,队列将永远损坏。 AB 确保可以重建状态并完成未完成的插入。

【讨论】:

  • 是的,你是对的。非常感谢您的详细解释。
猜你喜欢
  • 2020-07-21
  • 2017-06-27
  • 2019-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-07
  • 2020-04-21
  • 1970-01-01
相关资源
最近更新 更多