资讯专栏INFORMATION COLUMN

Treiber Stack简单分析

junfeng777 / 3056人阅读

摘要:在添加新项目时使用堆栈,将堆栈的顶部放在新项目之后。当从堆栈中弹出一个项目时,在返回项目之前,您必须检查另一个线程自操作开始以来没有添加其他项目。比较和交换将堆栈的头部设置为堆栈中旧的第二个元素,混合完整的数据结构。

Abstract

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提供的

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

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

    public E pop() {
        Node oldHead;
        Node 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 {
        public final E item;
        public Node next;
        public Node(E item) {
            this.item = item;
        }
    }
}
流程分析
PUSH操作


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

首先单链表保存了各个Stack中的各个元素,成员变量top持有了栈的栈顶元素。

当执行push操作时,首先创建一个新的元素为newHead,并让该新节点的next指针指向top节点(此时top=oldHead)。

最后通过CAS替换top=newHead,CAS的交换条件是top=oldHead

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

POP操作


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

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

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

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

参考:https://en.wikipedia.org/wiki...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70832.html

相关文章

  • FutureTask源码解析(2)——深入理解FutureTask

    摘要:本文的源码基于。人如其名,包含了和两部分。而将一个任务的状态设置成终止态只有三种方法我们将在下文的源码解析中分析这三个方法。将栈中所有挂起的线程都唤醒后,下面就是执行方法这个方法是一个空方 前言 系列文章目录 有了上一篇对预备知识的了解之后,分析源码就容易多了,本篇我们就直接来看看FutureTask的源码。 本文的源码基于JDK1.8。 Future和Task 在深入分析源码之前,我...

    Harpsichord1207 评论0 收藏0
  • Java多线程进阶(二二)—— J.U.C之synchronizer框架:Phaser

    摘要:分层支持分层一种树形结构,通过构造函数可以指定当前待构造的对象的父结点。当一个的参与者数量变成时,如果有该有父结点,就会将它从父结点中溢移除。当首次将某个结点链接到树中时,会同时向该结点的父结点注册一个参与者。 showImg(https://segmentfault.com/img/remote/1460000016010947); 本文首发于一世流云专栏:https://segme...

    Mr_zhang 评论0 收藏0
  • Java多线程奇幻之旅——CAS算法实现线程安全

    摘要:大多数保证线程安全的方法是添加各种类型锁,使用各种同步机制,用限制对共享的可变的类变量并发访问的方式来保证线程安全。只有保证这两条语句及中间语句以原子方式执行,才能避免多线程覆盖问题。 前言 对于线程安全,我们有说不尽的话题。大多数保证线程安全的方法是添加各种类型锁,使用各种同步机制,用限制对共享的、可变的类变量并发访问的方式来保证线程安全。文本从另一个角度,使用比较交换算法(Comp...

    jasperyang 评论0 收藏0
  • FutureTask源码分析

    摘要:从而可以启动和取消异步计算任务查询异步计算任务是否完成和获取异步计算任务的返回结果。原理分析在分析中我们没有看它的父类,其中有一个方法,返回一个,说明该方法可以获取异步任务的返回结果。 FutureTask介绍 FutureTask是一种可取消的异步计算任务。它实现了Future接口,代表了异步任务的返回结果。从而FutureTask可以启动和取消异步计算任务、查询异步计算任务是否完成...

    luqiuwen 评论0 收藏0
  • Java多线程进阶(四二)—— J.U.C之executors框架:Future模式

    摘要:本文首发于一世流云的专栏一模式简介模式是多线程设计模式中的一种常见模式,它的主要作用就是异步地执行任务,并在需要的时候获取结果。二中的模式在多线程基础之模式中,我们曾经给出过模式的通用类关系图。 showImg(https://segmentfault.com/img/bVbiwcx?w=1000&h=667); 本文首发于一世流云的专栏:https://segmentfault.co...

    marek 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<