前言

以前一直听说有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. CSS3 根据屏幕大小显示内容(@media)

    @media (min-width: 993px) {  .footer .addZ1{display:none;}  .footer .addZ2{display:none;}  .footer . ...

  2. VsCode下代码导航

    Visual Studio Code具有高效的代码编辑器,当与编程语言服务结合使用时,可以为您提供IDE的强大功能和文本编辑器的速度.在本主题中,我们将首先描述VS Code的语言智能功能(建议,参数 ...

  3. UVA725 Division 除法【暴力】

    题目链接>>>>>> 题目大意:给你一个数n(2 <= n <= 79),将0-9这十个数字分成两组组成两个5位数a, b(可以包含前导0,如02345 ...

  4. 中间人攻击工具ettercap

    中间人攻击工具ettercap (一).简介 (二).模块划分 1.Snifer 2.MITM 3.Filter 4.Log 5.Plugin (三).特性 (四).用户操作界面 (五).指定目标 ( ...

  5. 【Ray Tracing The Next Week 超详解】 光线追踪2-8 Volume

     Preface 今天有两个东东,一个是体积烟雾,一个是封面图 下一篇我们总结项目代码 Chapter 8:Volumes 我们需要为我们的光线追踪器添加新的物体——烟.雾,也称为participat ...

  6. Progressive web app理念及发展前景

    前一段时间微信推出微信小程序进行公测,着实火了一把,博得了大众的眼球,不明真相的吃瓜观众们纷纷围观,所谓的“微信小程序”,通俗的讲就是一种不需要下载安装即可使用的应用程序,脱离于app商店依托于浏览器 ...

  7. bootstrap中的行和列布局

    <!doctype html><html > <head> <meta charset="utf-8"> <link rel= ...

  8. .Net Core AES加密解密

    一.AES说明 高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准.这个标准用来替 ...

  9. java中关于AtomicInteger的使用

    在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字.而AtomicInteger则通过一种线程安全的加减操作接口.咳哟参考我之前写的一篇 ...

  10. vs2013修改书签(vs书签文件位置)

    visual studio 2013 的书签功能很好用,可以记录一些代码的位置:方便查阅: 不过当项目被他人修改过后,svn update 更新过后,书签的文件行号不变,但是已经不再是原来记录的哪一行 ...