Java 为堆栈实现正确使用AtomicReference.compareAndSet

Java 为堆栈实现正确使用AtomicReference.compareAndSet,java,concurrency,atomic,Java,Concurrency,Atomic,我正在试验java.util.concurrent,并试图找出如何正确使用AtomicReference.compareAndSet来管理对单个共享状态单元的并发访问 特别是:compareAndSet的以下用法是否正确和安全?有什么陷阱吗 我的测试类是一个基于节点链表的简单堆栈 public class LinkedStack<T> { AtomicReference<Node<T>> topOfStack=new AtomicReference<

我正在试验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;
    }
  } 
  ...................
}
公共类LinkedStack{
AtomicReference topOfStack=新的AtomicReference();
公共T推送(TE){
while(true){
Node oldTop=topOfStack.get();
节点newTop=新节点(e,oldTop);
如果(topOfStack.compareAndSet(oldTop,newTop))中断;
} 
返回e;
}
公共广播电台{
while(true){
Node oldTop=topOfStack.get();
如果(oldTop==null)抛出新的EmptyStackException();
节点newTop=oldTop.next;
if(topOfStack.compareAndSet(oldTop,newTop))返回oldTop.object;
} 
}
私有静态最终类节点{
最终的T对象;
最终节点下一步;
私有节点(T对象,下一个节点){
this.object=object;
this.next=next;
}
} 
...................
}

是的,这正是它应该使用的方式

也许以下语法会更优雅:

Node<T> oldTop = null;
Node<T> newTop = null;
do {
    oldTop=topOfStack.get();
    newTop=new Node<T>(e,oldTop);
} while (!topOfStack.compareAndSet(oldTop, newTop));
节点oldTop=null;
节点newTop=null;
做{
oldTop=topOfStack.get();
newTop=新节点(e,oldTop);
}而(!topOfStack.compareAndSet(oldTop,newTop));
一切看起来都很好(从axtavt的说法来看,这并不是什么新鲜事),我认为值得注意的一点是,pop或push的失败率现在是ConcurrentLinkedQueue的两倍。当我说fail时,我的意思是你必须再次在while循环中执行,假设前面有另一个线程弹出或推动


虽然这可能超出了您试图实现的范围,但某种退避协议将有助于在高争用情况下提高性能。

它的当前形式是正确的

您的结构称为Treiber堆栈。它是一个简单的线程安全的无锁结构,存在单点争用(
topOfStack
)问题,因此扩展性很差(在争用下,它会受到攻击,这与MMU的性能不太好)。如果争用可能很低,但仍然需要线程安全性,那么这是一个很好的选择


有关缩放堆栈算法的更多信息,请参阅Danny Hendler、Nir Shavit和Lena Yerushalmi的“”。

感谢您的确认!我想按照你的建议去做,但是Java对oldTop和newTop的作用域规则似乎不允许这样做……是的,我支持@mikera,因为我更喜欢具有奇怪出口的适当范围,而不是相反的方式。但我同意你的评估。唯一让我停顿的不是你如何使用原子引用,而是你从推送返回的内容。我宁愿
push
不返回任何内容或被掩埋的值,也不希望返回您正在推送的值。@Mark,是的,这也让我停顿了一下,但我只是想与Java的标准堆栈保持一致——承认它是一个有点奇怪的返回定义。出于许多原因,最好假装类不存在(它不是一个应该存在的接口,它使用从向量继承而不是组合,等等)。我会从界面中得到您的提示,而不是从
堆栈
中选择哪个实现。它是在Java 6中引入的,
Stack
的Javadoc被修改为建议在
Stack
上使用
Deque
@Mark-是的,你是对的,Queue或Deque显然是一个更好的模型:-)我认为旧堆栈现在可能应该被弃用了…..像push这样的方法返回push对象的原因是它允许方法调用的简单链接:Stack.push(新整数(5)).intValue();如果他们进行了收集#添加以返回添加的对象,那么它将是一致的。谢谢!我特别接受你的答案,因为了解这个小结构的实际名称很有趣:-)