Treiber Stack简单分析

Abstract

Treiber Stack Algorithm是一个可扩展的无锁栈,利用细粒度的并发原语CAS来实现的,Treiber Stack在 R. Kent Treiber在1986年的论文Systems Programming: Coping with Parallelism中首次出现。算法

基本原理

该算法的基本原理是:只有当您知道要添加的项目是自开始操做以来惟一添加的项目时,才会添加新的项目。 这是经过使用比较和交换完成的。 在添加新项目时使用堆栈,将堆栈的顶部放在新项目以后。 而后,将这个新构造的头元素(旧头)的第二个项目与当前项目进行比较。 若是二者匹配,那么你能够将旧头换成新头,不然就意味着另外一个线程已经向堆栈添加了另外一个项目,在这种状况下,你必须再试一次。数据结构

当从堆栈中弹出一个项目时,在返回项目以前,您必须检查另外一个线程自操做开始以来没有添加其余项目。并发

正确性

在某些语言中,特别是那些没有垃圾回收的语言,Treiber栈可能面临ABA问题。当一个进程要从堆栈中移除一个元素时(就在下面的pop例程比较和设置以前),另外一个进程能够改变堆栈,使得头部是相同的,可是第二个元素是不一样的。比较和交换将堆栈的头部设置为堆栈中旧的第二个元素,混合完整的数据结构。可是,因为Java运行时提供了更强大的保证,因此此页面上的Java版本不受此问题的影响(新建立的不混淆的对象引用不可能与任何其余可到达的对象引用相同)。测试

对诸如ABA之类的故障进行测试可能会很是困难,由于有问题的事件序列很是少见。this

Java示例

下面是Java中Treiber Stack的实现,它基于Java Concurrency in Practice提供的spa

public class ConcurrentStack<E> {
    private AtomicReference<Node<E>> top = new AtomicReference<>();

    public void push(E item) {
        Node<E> newHead = new Node<>(item);
        Node<E> oldHead;
        do {
            oldHead = top.get();
            newHead.next = oldHead;
        } while (!top.compareAndSet(oldHead, newHead));
    }

    public E pop() {
        Node<E> oldHead;
        Node<E> newHead;
        do {
            oldHead = top.get();
            if (oldHead == null)
                return null;
            newHead = oldHead.next;
        } while (!top.compareAndSet(oldHead, newHead));
        return oldHead.item;
    }

    private static class Node<E> {
        public final E item;
        public Node<E> next;
        public Node(E item) {
            this.item = item;
        }
    }
}

流程分析

PUSH操做

图片描述
根据上述的描述作图如上,并分析其工做流程。线程

  1. 首先单链表保存了各个Stack中的各个元素,成员变量top持有了栈的栈顶元素。
  2. 当执行push操做时,首先建立一个新的元素为newHead,并让该新节点的next指针指向top节点(此时top=oldHead)。
  3. 最后经过CAS替换top=newHead,CAS的交换条件是top=oldHead
  4. 当条件知足后,操做后的状态以下:

图片描述

POP操做

图片描述
根据上述的描述作图如上,并分析其工做流程。指针

  1. 当执行pop操做时,建立一个新的指针,该指针指向topnext元素。
  2. 而后经过CAS替换top=newHead,CAS的交换条件是top=oldHead

3.当条件知足后,操做后的状态以下:
图片描述
参考:https://en.wikipedia.org/wiki...code