正确使用AtomicReference.compareAndSet进行堆栈实现

mik*_*era 4 java concurrency atomic

我正在尝试使用java.util.concurrent并试图找出如何正确使用AtomicReference.compareAndSet来管理对单个共享状态单元的并发访问.

特别是:compareAndSet的以下用法是否正确且安全?任何陷阱?

我的测试类是一个基于链接节点列表的简单堆栈.

public class LinkedStack<T> {

  AtomicReference<Node<T>> topOfStack=new AtomicReference<Node<T>>();

  public T push(T e) {
    while(true) {
      Node<T> oldTop=topOfStack.get();
      Node<T> newTop=new Node<T>(e,oldTop);
      if (topOfStack.compareAndSet(oldTop, newTop)) break;
    } 
    return e;
  }

  public T pop() {
    while(true) {
      Node<T> oldTop=topOfStack.get();
      if (oldTop==null) throw new EmptyStackException();
      Node<T> newTop=oldTop.next;
      if (topOfStack.compareAndSet(oldTop, newTop)) return oldTop.object;
    } 
  }

  private static final class Node<T> {
    final T object;
    final Node<T> next;

    private Node (T object, Node<T> next) {
      this.object=object;
      this.next=next;
    }
  } 
  ...................
}
Run Code Online (Sandbox Code Playgroud)

axt*_*avt 5

是的,这正是应该如何使用的.

也许以下语法会更优雅:

Node<T> oldTop = null;
Node<T> newTop = null;
do {
    oldTop=topOfStack.get();
    newTop=new Node<T>(e,oldTop);
} while (!topOfStack.compareAndSet(oldTop, newTop));
Run Code Online (Sandbox Code Playgroud)

  • 是的,我和@mikera在一起,因为我更喜欢适当的范围而不是相反的方式.但我同意你的评估. (2认同)

Jed*_*ith 4

它目前的形式是正确的。

您的结构称为 Treiber 堆栈。它是一种简单的线程安全无锁结构,但存在单点争用 ( topOfStack) 的问题,因此往往会严重扩展(在争用情况下,它会崩溃,这与 MMU 的配合不太好)。如果争用可能较低但仍需要线程安全,那么这是一个不错的选择。

有关扩展堆栈算法的进一步阅读,请参阅 Danny Hendler、Nir Shavit 和 Lena Yerushalmi 撰写的“可扩展无锁堆栈算法 (pdf) ”。