Treiber Stack

摘要

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

基本原理

该算法的基本原理是:只有当您知道要添加的项目是自开始操作以来唯一添加的项目时,才会添加新的项目。 这是通过使用比较和交换完成的。 在添加新项目时使用堆栈,将堆栈的顶部放在新项目之后。 然后,将这个新构造的头元素(旧头)的第二个项目与当前项目进行比较。 如果两者匹配,那么你可以将旧头换成新头,否则就意味着另一个线程已经向堆栈添加了另一个项目,在这种情况下,你必须再试一次。 当从堆栈中弹出一个项目时,在返回项目之前,您必须检查另一个线程自操作开始以来没有添加其他项目。

正确性

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

Java示例

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

ini 复制代码
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。

当条件满足后,操作后的状态如下:

POP操作

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

1.当执行pop操作时,创建一个新的指针,该指针指向top的next元素。

2.然后通过CAS替换top=newHead,CAS的交换条件是top=oldHead。

3.当条件满足后,操作后的状态如下:

作者:码农

链接:juejin.cn/post/693320...

来源:稀土掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关推荐
极光代码工作室1 小时前
基于SpringBoot的课程预约系统
java·springboot·web开发·后端开发
Leighteen1 小时前
`try-finally` 里的 `return`:为什么 `finally` 会悄悄改掉返回值、吞掉异常
java·开发语言
名字还没想好☜2 小时前
Go 的 time.After 在 select 循环里内存泄漏:定时器堆积原理与 timer.Reset 正确姿势
java·数据库·golang·go·goroutine
圆山猫2 小时前
[Virtualization](三):RISC-V H-extension 与 Guest 执行模式
android·java·risc-v
caishenzhibiao3 小时前
期货先行者主图 同花顺期货通指标
java·c语言·c#
史呆芬3 小时前
分布式事务实战:微服务跨服务数据一致性解决方案
java·后端·spring cloud
IT界的老黄牛3 小时前
限流命中后该怎么办:直接丢、阻塞等待、延迟重投三种姿势的取舍
java·rocketmq·redisson·令牌桶·削峰·分布式限流
daad7774 小时前
记录matlab状态机demo
java·网络·matlab
牡丹雅忻14 小时前
AES 加密模式演进:从 ECB、CBC 到 GCM 的 C# 深度实践
java·开发语言·c#
学者猫头鹰5 小时前
Java 8 核心新特性
java