前言

以前一直听说有Disruptor这个东西,都说性能很强大,所以这几天自己也看了一下。
下面是自己的学习笔记,另外推荐几篇自己看到写的比较好的博客:
Disruptor——一种可替代有界队列完成并发线程间数据交换的高性能解决方案
Disruptor3.0的实现细节

DIsruptor的底层性能如此牛掰

  1. 数据结构层面:使用环形结构、数组、内存预加载
  2. 单线程写方式、内存屏障
  3. 消除伪共享(填充缓存行)
  4. 序号栅栏(SequenceBarrier)配合使用来消除锁和CAS

高性能之道-数据结构-内存加载机制

  1. RingBuffer使用数组Object[] entries作为存储元素,如下图所示

高性能之道-内核-使用单线程写

  1. Disruptor的RingBuffer,之所以可以做到完全无锁,也是因为 ”单线程写“, 这是所有”前提的前提“。离开了这个前提条件,没有任何技术可以做到完全无锁
  2. Redis、Netty等等高性能技术框架的设计都是这个核心思想

高性能之道-系统内存优化-内存屏障

  1. 要正确的实现无锁,还需要另一个关键技术:内存屏障。
  2. 对应到Java语言,就是valotile变量与happens before语义。
  3. 内存屏障-Linux的smp_wmb()/smp_rmb()

高性能之道-系统缓存优化-消除伪共享

  1. 缓存系统中是以缓存行(cache line)为单位存储的
  2. 缓存行是2的整数幂个连续字节,一般为32-256个字节
  3. 最常见的缓存行大小是64个字节
  4. 当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行
  5. 就会无意中影响彼此的性能,这就是伪共享 -- 对应源码中的:Sequence:

Disruptor核心-Sequence

  1. Sequence可以看成是一个AtomicLong,用于标识进度
  2. 还有另外一个目的就是防止不同Sequence之间CPU缓存伪共享(False Sharing)的问题-- 对应源码中的:Sequence:

高性能之道-算法优化-序号栅栏机制

  1. 我们在生产者进行投递Event的时候,总会使用:long sequence = ringBuffer.next();
  2. Disruptor 3.0中,序号栅栏SequenceBarrier和序号Sequence搭配使用,协同和管理消费者与生产者的工作节奏,避免了锁和CAS的使用
  3. 在Disruptor3.0中,各个消费者和生产者持有自己的序号,这些序号的变化必须满足如下基本条件:-- 参见源码:SingleProducerSequencer
    a. 消费者的序号数值必须小于生产者序号数值;b. 消费者序号数值必须小于其前置(依赖关系)消费者的序号数值; c. 生产者序号数值不能大于消费者最小的序号数值以避免生产者速度过快,将还未来得及消费的消息覆盖

WatiStrategy等待策略

  1. Disruptor之所以可以说是高性能,其实也有一部分原因取决于它的等待策略的实现:WaitStrategy接口:
    -- 查看源码BlockingWaitStrategy
    -- 查看源码YieldingWaitStrategy

Disruptor核心-EventProcessor

  1. EventProcessor:主要时间循环,处理Disruptor中的Event,拥有消费者的Sequence
  2. 它有一个实现类是BatchEventProcessor,包含了event loop有效的实现,并且将回调到一个EventHandler接口的思想对象 -- 参见BatchEventProcessor

源码解读

Disruptor:Disruptor的入口,主要封装了环形队列RingBuffer、消费者集合ConsumerRepository的引用;主要提供了获取环形队列、添加消费者、生产者向RingBuffer中添加事件(可以理解为生产者生产数据)的操作;
RingBuffer:Disruptor中队列具体的实现,底层封装了Object[]数组;在初始化时,会使用Event事件对数组进行填充,填充的大小就是bufferSize设置的值;此外,该对象内部还维护了Sequencer(序列生产器)具体的实现;
Sequencer:序列生产器,分别有MultiProducerSequencer(多生产者序列生产器) 和 SingleProducerSequencer(单生产者序列生产器)两个实现类。上面的例子中,使用的是SingleProducerSequencer;在Sequencer中,维护了消费者的Sequence(序列对象)和生产者自己的Sequence(序列对象);以及维护了生产者与消费者序列冲突时候的等待策略WaitStrategy;
Sequence:序列对象,内部维护了一个long型的value,这个序列指向了RingBuffer中Object[]数组具体的角标。生产者和消费者各自维护自己的Sequence;但都是指向RingBuffer的Object[]数组;
Wait Strategy:等待策略。当没有可消费的事件时,消费者根据特定的策略进行等待;当没有可生产的地方时,生产者根据特定的策略进行等待;
Event:事件对象,就是我们Ringbuffer中存在的数据,在Disruptor中用Event来定义数据,并不存在Event类,它只是一个定义;
EventProcessor:事件处理器,单独在一个线程内执行,判断消费者的序列和生产者序列关系,决定是否调用我们自定义的事件处理器,也就是是否可以进行消费;
EventHandler:事件处理器,由用户自定义实现,也就是最终的事件消费者,需要实现EventHandler接口;

RingBuffer:


Sequence:

这个里面缓存行的填充很经典,设计成前7后7 Long类型来填充,保证消除伪共享。
使用空间换时间,避免伪共享。Java8中使用@sun.misc.Contended 来消除伪共享,在运行时需要设置JVM启动参数:-XX:-RestrictContended

这里前7后7加上本身的Value值,总共是有15个Long元素,无论如何拆分,Value和预填充的Long型数据一定会处于单独的一个缓存行。

SingleProducerSequencer

这里就是用简单的if else判断,就避免了加锁,CAS的消耗,这里是使用序号栅栏,通过巧妙的算法+自旋操作来实现等待的操作。
解析如下图:

其中可以自己写代码去debug,创建ringBuffer长度为4,消费者阻塞在第0个元素的消费中。然后生产者再生产第5个元素的时候就会进行自旋等待。

BlockingWaitStrategy

BatchEventProcessor

waitFor 可以参考上面的BlockingWaitStrategy 的waitFor() 方法

Disruptor学习笔记的更多相关文章

  1. Disruptor学习笔记(一):基本原理和概念

    一.Disruptor基本原理 在多线程开发中,我们常常遇到这样一种场景:一些线程接受用户请求,另外一些线程处理这些请求.比如日志处理中的日志输入和告警.这种典型的生产者消费者场景十分常见,而生产者消 ...

  2. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  3. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  4. PHP-会员登录与注册例子解析-学习笔记

    1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...

  5. 2014年暑假c#学习笔记目录

    2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...

  6. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  7. seaJs学习笔记2 – seaJs组建库的使用

    原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...

  8. CSS学习笔记

    CSS学习笔记 2016年12月15日整理 CSS基础 Chapter1 在console输入escape("宋体") ENTER 就会出现unicode编码 显示"%u ...

  9. HTML学习笔记

    HTML学习笔记 2016年12月15日整理 Chapter1 URL(scheme://host.domain:port/path/filename) scheme: 定义因特网服务的类型,常见的为 ...

随机推荐

  1. C#自动生成XML文件

    命名空间:using System.Xml; 程序集:  System.Xml(在 System.Xml.dll 中). 涉及到的类和方法: XmlDeclaration 类:表示 XML 声明节点: ...

  2. Python3.X 安装Scrapy

    安装Scrapy有两种方法: 1.pip install Scrapy 这种方式按道理来说是最简洁最快速的,但是有的时候安装不成功,只能更换一种方式,下载源文件安装的方式,详见下面一步. 2.下载文件 ...

  3. phpunit

    教程及文档: https://www.jianshu.com/p/abcca5aa3ad6 http://www.phpunit.cn/manual/current/zh_cn/phpunit-boo ...

  4. c# 深入探索之CLR

    概念: CLR : 公共语言运行时(Common Language Runtime) 是一个可由多种编程语言使用的"运行时",它负责资源管理(内存分配和垃圾收集等),并保证应用和底 ...

  5. 关于 tensorflow-gpu 中 CUDA 和 CuDNN 版本适配问题

    问题 今天在使用 tensorflow-yolov3 的时候,发现报错 Loaded runtime CuDNN library: but source was compiled with: . Cu ...

  6. IdentityServer4-快速入门

    一.设置和概述 二.使用客户端凭证保护API 三.使用密码保护API 四.使用OpenID Connect添加用户验证 五.添加对外部认证的支持 六.切换到Hybrid Flow并添加API访问权限 ...

  7. JS中集合对象(Array、Map、Set)及类数组对象的使用与对比

    原文地址 在使用js编程的时候,常常会用到集合对象,集合对象其实是一种泛型,在js中没有明确的规定其内元素的类型,但在强类型语言譬如Java中泛型强制要求指定类型. ES6引入了iterable类型, ...

  8. springmvc框架javax.servlet.http.HttpServletResponse出现小红叉

    需要在项目点右键配置属性--->Library--->server runtime--->但是配置不能成功,原因是没有在windows下配置过runtime environment, ...

  9. 构造函数与getter和setter的区别

    构造函数是用于初始化类的属性,且只有在创建对象时才会调用构造函数,用于给对象分配地址 无参的构造函数,创建对象时默认调用,当程序没有明确写出有参的构造函数,系统会默认的创建一个. 有参的构造函数,创建 ...

  10. Activex控件的IObjectSafety接口问题

    我的05年做流氓插件的时候,就注意到了这个问题,只要注册表加入 类似的就可以  HKEY_CLASSES_ROOT\Component    Categories\{7DD95801-9882-11C ...