纯CAS为啥比加锁要快?

同样是修改数据,一个采用加锁的方式保证原子性,一个采用CAS的方式保证原子性。

都是能够达到目的的,但是常用的锁(例如显式的Lock和隐式的synchonized),都会把获取不到锁的线程挂起,相对于CAS的不挂起,多了挂起和唤醒的开销。

题外话:CAS与锁的关系

CAS只是在这个场景下,比使用锁来得更纯粹,因为只做数据更新,所以开销更少。但是业务上为了保证一系列操作的原子性,还是要使用锁的。而且锁的底层实现,也依赖于类似于CAS这样的原子性操作。

尾指针是如何管理的,如何防止覆盖旧数据?

别的帖子都说RingBuffer中不维护尾指针,尾指针由消费者维护(所谓维护指针,就是修改、移动指针)其实这一句话有点误导性,如果RingBuffer不知道尾部在哪里,那它的数据存储肯定就会出问题,例如把还没消费过的数据给覆盖了。

确实,消费者会自行维护自己的消费指针(消费者指针是消费者消费过的最后一条数据的序号,下一篇中会详细讲到),RingBuffer也不会去干涉消费者指针的维护,但是它会引用所有消费者的指针,读取他们的值,以此作为“尾部”的判断依据。实际上就是最慢的那个消费者作为边界

我们直接来看代码,这个是RingBuffer的publishEvent方法,我们看到,它首先取得一个可用的序列号,然后再将数据放入该序列号的对应位置中。

@Overridepublic void publishEvent(EventTranslatortranslator)

{//1.先通过原子操作,得到一个可用的序号

final long sequence =sequencer.next();//2.将该序号对应位置的元素进行转换,接着发布

translateAndPublish(translator, sequence);

}

我们来看看这个序列号是如何取得的。我们先看Sequencer的SingleProducerSequencer实现。这里就是判断如果生产者新指针的位置是否会超过尾部,如果超过尾部就挂起片刻,后续再尝试(生产者的等待方式是固定的,不像消费者有一个等待策略)

这里附上几个图可能更好理解:(右边是后续补充的用“画图”画的,对单元格添加一些颜色进行区分)

情况1:队列已满,生产者尝试使用新序号14,但由于(14 - 8 = 6),由于最慢的消费者目前消费的最后一条数据的序号是5,5号之后的数据还没被消费,6 > 5,所以序号14还不能用。生产者线程挂起,下次再次尝试。

情况2:消费者1消费了序号6的数据。(14 - 8 = 6) 不大于 6,这时序号14可用,生产者得到可用的序号。

@Overridepublic longnext()

{return next(1);

}/***@seeSequencer#next(int)*/@Overridepublic long next(intn)

{if (n < 1 || n >bufferSize)

{throw new IllegalArgumentException("n must be > 0 and < bufferSize");

}long nextValue = this.nextValue; //当前RingBuffer的游标,即生产者的位置指针

long nextSequence = nextValue +n;long wrapPoint = nextSequence - bufferSize; //减掉一圈

long cachedGatingSequence = this.cachedValue; //上一次缓存的最小的消费者指针//条件1:生产者指针的位置超过当前消费最小的指针//条件2:为特殊情况,这里先不考虑,详见:

if (wrapPoint > cachedGatingSequence || cachedGatingSequence >nextValue)

{

cursor.setVolatile(nextValue);//StoreLoad fence

longminSequence;//再次遍历所有消费者的指针,确认是否超过//如果超过,则等待

while (wrapPoint > (minSequence =Util.getMinimumSequence(gatingSequences, nextValue)))

{

LockSupport.parkNanos(1L); //TODO: Use waitStrategy to spin?

}this.cachedValue =minSequence;

}this.nextValue =nextSequence;returnnextSequence;

}

另外对于多生产者的情况,在不会越界的情况下,需要通过CAS来保证获取序号的原子性。具体可以查看MultiProducerSequencer的next方法。

消费者指针是如何读取的?

RingBuffer如何知道有哪些消费者?哪些gatingSequense是从哪里来的?

在构建RingBuffer注册处理类的时候,就将消费者Sequense注册到RingBuffer中了。

看代码的话,定位到gatingSequences在AbastractSequencer,对应的有个addGatingSequenses方法用于注入gatingSequence

public abstract class AbstractSequencer implementsSequencer {//...

protected volatile Sequence[] gatingSequences = new Sequence[0];

@Overridepublic final voidaddGatingSequences(Sequence... gatingSequences)

{

SequenceGroups.addSequences(this, SEQUENCE_UPDATER, this, gatingSequences);

}//...

}

再查看addGatingSequences被调用的地方,即通过RingBuffer的方法,设置到Sequencer中,这个Sequencer是生产者使用的序号管理器

public final class RingBuffer extends RingBufferFields implements Cursored, EventSequencer, EventSink{//...

protected finalSequencer sequencer;public voidaddGatingSequences(Sequence... gatingSequences) {

sequencer.addGatingSequences(gatingSequences);

}//...

}

而RingBuffer的addGatingSequence则在Disruptor配置处理器的时候被调用

public class Disruptor{//...

private final RingBufferringBuffer;private final ConsumerRepository consumerRepository = new ConsumerRepository<>();public EventHandlerGroup handleEventsWith(finalEventProcessor... processors)

{for (finalEventProcessor processor : processors)

{

consumerRepository.add(processor);

}final Sequence[] sequences = newSequence[processors.length];for (int i = 0; i < processors.length; i++)

{

sequences[i]=processors[i].getSequence();

}

ringBuffer.addGatingSequences(sequences);return new EventHandlerGroup<>(this, consumerRepository, Util.getSequencesFor(processors));

}//...

}

缓存的意义是什么?

我们看到在SiingleProducerSequencer的next方法中,会缓存上一次的消费者最小序列号,这有什么用呢?

用途就是不需要每次都读取各消费者的序号,只要没超过上一次的最小值的地方都可以直接分配,如果超过了,则进行再次判断

为啥读取最小值不需要保证原子性?

看了这个获取最小消费序号的,可能会奇怪,为啥这个操作不需要上锁,这个不是会获取到旧值吗?

确实,这个最小值获取到的时候,实际上数值已经变更。但是由于我们的目的是为了防止指针越位,所以用旧值是没有问题的。(旧值<=实际上的最小值)

public static long getMinimumSequence(final Sequence[] sequences, longminimum)

{for (int i = 0, n = sequences.length; i < n; i++)

{long value =sequences[i].get();

minimum=Math.min(minimum, value);

}returnminimum;

}

c ringbuffer 源码_【源码】RingBuffer(一)——生产者相关推荐

  1. 源码_网站源码_游戏源码_源码下载-开源之家

    开源之家 - 建站6年,站内有海量网站源码(asp源码,php源码,.net源码),游戏源码(VC++源码,C#源码,C++源码),商业源码,网站模板,微信源码,区块链源码,网游源码提供给大家下载. ...

  2. python手工打码_打码兔和超人打码python版

    1.[代码][Python]代码 # coding:utf-8 from ctypes import * import requests import json import random impor ...

  3. 扫码点餐小程序源码_扫码点餐小程序有什么用?怎么制作?

    现在小程序扫码点餐服务已经越来越普及,当用户需要点餐时,无需麻烦服务人员,只需扫描餐桌上或者海报上的小程序码,就能快速点餐下单.这样不仅节约了排队时间,也提高了商家自己的服务效率. 上线了小程序案例, ...

  4. flask项目源码_源码解读:Flask上下文与代理模式

    在上一节中,我跟大家一起深入了解了一下Python的「上下文管理器 」.而今天呢,还是和上下文有关的话题.只不过这里的上下文和上一节的内容有点不一样,上下文管理器是管理代码块级别的上下文,而今天要讲的 ...

  5. php-mcrypt 源码_源码方式安装php扩展mcrypt

    本文实际上是在CentOS下进行的,原理和在Ubuntu下源码安装一样,下图首先示例mcrypt和php的依赖关系 基本原理是:首先使mcrypt软件能够运行,然后安装php扩展模块,并在php.in ...

  6. mysql php apache源码_源码安装apache+mysql+php

    源码安装apache+mysql+php #!/bin/sh #byliangz at 2010-08-14 #环境: #     1. CentOS5.5或RHEL5.4,配置好IP地址,主机名等信 ...

  7. mysql查询优化器源码_源码下载网浅析MySQL 查询优化器

    源码下载网浅析MySQL 查询优化器 时间:2019-01-18 17:45作者:网友投稿 优化器(The Optimizer) 这篇描述MySQL查询优化器的工作原理.MySQL查询优化器主要为执行 ...

  8. java pop邮件 源码_[源码和文档分享]基于JavaMail的邮件收发系统

    摘 要 电子邮件在当今社会中扮演了一个很重要的角色.越来越多的人在使用它.而且用它的人数势必会继续增加.本文介绍了Javamail邮件收发系统的开发背景,对国内外现有的多种成熟的电子邮件系统进行分析和 ...

  9. android ble 助手源码_[源码和文档分享]基于Android的生活助手APP的设计与实现

    摘 要 随着移动互联网的高速发展,Android操作系统在移动设备中地位已经被牢牢稳固.然而大量的Android设备高速普及过程中,与其配套的Android应用的开发速度和项目质量极为令人担忧.本课题 ...

  10. 图书管理系统 java 源码_[源码和文档分享]基于C语言和SQL SERVER数据库实现的图书管理系统...

    摘 要 本文根据<数据库应用系统设计>课程要求而做.选择图书馆管理系统设计与开发是因为觉得图书馆管理系统对我们的帮助很大,并且经常去图书馆,对图书馆的大部分功能及流程还是比较了解,而且现在 ...

最新文章

  1. C语言 数据结构与算法 一
  2. 使用Cython库包对python的py文件(源码)进行加密,把python的.py文件生成.so文件并调用
  3. DPDK 内存池rte_mempool实现(二十三)
  4. python猜数字循环_python-练习实现猜数字的循环
  5. Windows Server 2008虚拟化功能解析
  6. Recurrent Neural Networks
  7. 谷歌再遭反垄断起诉:曾试图“扼杀”三星应用商店!
  8. 【模板】吉司机线段树(势能线段树)
  9. Unity 获取文件夹下所有文件夹/文件
  10. springbootTest为什么整合dubbo后无法使用
  11. JS完成注册页面的省市联动(JS内置对象全局函数,select标签操作)
  12. 数学建模:线性规划及 Python 求解
  13. 学习如何避免10种最常见的C#误区
  14. 【ffmpeg】——批量合并视频
  15. 电磁阀单电控与双电控区别
  16. 2019年中国互联网企业100强
  17. 【Linux】(用户不在sudoers文件中……) 添加用户到sudoers
  18. 叽叽歪歪JS乱七八糟的方法——web(二)
  19. Qt Mediaplayer videoplayer 例子工程 Media Player Example 应用过程中出现的问题(一)视频无法播放
  20. 计算机网络谢希仁(第七版)

热门文章

  1. 实战:用Concourse实现端到端的蓝绿部署
  2. 长安汽车2015款悦翔V7好用的凯立德主程序
  3. mini车f和r的区别_MINI车型这么多特殊版本怎么区分?
  4. Python调用Windows API实现文本朗读
  5. 弱电流源是怎么实现的,咱们来仿个真
  6. ES Analyzer
  7. electron一键生成项目图标
  8. 蒸汽流量计算软件_涡街流量计的基本测量原理及选型
  9. jsp人事工资管理系统 课程设计
  10. 5款Chrome插件,第1款绝对良心!