Disruptor详细介绍
Disruptor是什么?
Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题
(在性能测试中发现竟然与I/O操作处于同样的数量级)。
基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注。
2011年,企业应用软件专家Martin Fowler专门撰写长文介绍Disruptor。同年Disruptor还获得了
Oracle官方的Duke大奖。
目前,包括Apache Storm、Camel、Log4j 2在内的很多知名项目都应用了Disruptor以获取高性能。
Java内置队列的问题
介绍Disruptor之前,我们先来看一看常用的线程安全的内置队列有什么问题。
Java的内置队列如下表所示
队列 | 有界性 | 锁 | 数据结构 |
---|---|---|---|
ArrayBlockingQueue | bounded | 加锁 | arraylist |
LinkedBlockingQueue | optionally-bounded | 加锁 | linkedlist |
ConcurrentLinkedQueue | unbounded | 无锁 | linkedlist |
LinkedTransferQueue | unbounded | 无锁 | linkedlist |
PriorityBlockingQueue | unbounded | 加锁 | heap |
DelayQueue | unbounded | 加锁 | heap |
Disruptro与ArrayBlockingQueue性能对比
https://my.oschina.net/u/3434392/blog/3099183
前置知识
Cpu结构
伪共享(False Sharing)
CPU的缓存系统是以缓存行(cache line)为单位存储的,一般的大小为64bytes。
在多线程程序的执行过程中,存在着一种情况,多个需要频繁修改的变量 存在同一个缓存行当中
假设:有多个线程同时访问修改X和Y,X和Y在同一个缓存行上,这两个线程分别在不同的CPU上执行。
两个线程分别修改了X和Y,发现刚刷入的变量已经改变,缓存行失效,又需要重新从L3中读取。
为了减少这种情况的发生,其实就是避免这几个变量在同一个缓存行中
伪共享的本质:
对缓存行中的单个变量进行修改了,导致整个缓存行其他不相关的数据也就失效了,需要从主存重新加
载
如果 其中有 volatile 修饰的变量,需要保证线程可见性的变量,还需要进入 缓存与数据一致性的保障
流程, 如mesi协议的数据一致性保障 用了其他变量的 Core的缓存一致性。
如何解决伪共享
其中一个手段就是通过填充(Padding)数据的形式,来保证本应有可能位于同一个缓存行的两个变量,在被多线程访问时必定位于不同的缓存行。disruptor 无锁框架就是这么干的。
JAVA 8中添加了一个@Contended的注解,添加这个的注解,将会在自动进行缓存行填充
@Contended
public long padVar;
在Java 8中,使用@Contended注解的对象或字段的前后各增加128字节大小的padding,使用2倍于大
多数硬件缓存行的大小来避免相邻扇区预取导致的伪共享冲突。我们目前的缓存行大小一般为64Byte,
这里Contended注解为我们前后加上了128字节绰绰有余。
注意:如果想要@Contended注解起作用,需要在启动时添加JVM参数-XX:-RestrictContended
可见至少在JDK1.8以上环境下, 只有@Contended注解才能解决伪共享问题, 但是消耗也很大, 占用
了宝贵的缓存, 用的时候要谨慎。
@Contended 注释还可以添加在类上,每一个成员,都会加上。
启示
当某个变量,竞争激烈,在高并发环境下,被大量读写,可以使用消除伪共享的机制,提高性能
Disruptor
Disruptor如何解决伪共享
在Disruptor中有一个重要的类Sequence,该类包装了一个volatile修饰的long类型数据value,无论是Disruptor中的基于数组实现的缓冲区RingBuffffer,还是生产者,消费者,都有各自独立的Sequence。
Sequence中的 long类型的Value,左右都用7个long类型做了填充,保证了无论Cpu如何读取该数据,都能让真正的Value始终保持在一个缓存行中。
Disruptor如何实现高性能
Disruptor实现高性能主要体现了去掉了锁,采用CAS算法,同时内部通过环形队列实现有界队列。
- 环形数据结构
数组元素不会被回收,避免频繁的GC,所以,为了避免垃圾回收,采用数组而非链表。
同时,数组对处理器的缓存机制更加友好。
- 元素位置定位
数组长度2^n,通过位运算,加快定位的速度。
下标采取递增的形式。不用担心index溢出的问题。
index是long类型,即使100万QPS的处理速度,也需要30万年才能用完。
- 无锁设计
采用CAS无锁方式,保证线程的安全性
每个生产者或者消费者线程,会先申请可以操作的元素在数组中的位置,申请到之后,直接在该位
置写入或者读取数据。整个过程通过原子变量CAS,保证操作的线程安全。
- 属性填充:
通过添加额外的无用信息,避免伪共享问题
- 内存预加载:
RingBuffer使用数组Object[] entries来存储元素:
-初始化RingBuffer时,会将所有数组元素entries的指定为特定的事件Event参数,此时Event中的detail属性为nul
-生产者向RingBuffer写入消息时 ,RingBuffer不是直接将数组元素entries指向Event对象,而是先获取Event对象,更改Event对象中的detail属性
-消费者在消费时,也是从RingBuffer中读取Event, 读取Event对象中的detail属性
-由此可见,在生产和消费过程中 ,RingBuffer中的数组元素entries没有发生任何变化,没有产生临时对象,数组中的元素一直存活,直到RingBuffer消亡
Disruptor 核心组件
Ring Buffffer
如其名,环形的缓冲区。
曾经 RingBuffffer 是 Disruptor 中的最主要的对象,但从3.0版本开始,其职责被简化为仅仅负责对通过
Disruptor 进行交换的数据(事件)进行存储和更新。
在一些更高级的应用场景中,Ring Buffffer 可以由用户的自定义实现来完全替代。
Sequence
通过顺序递增的序号来编号管理通过其进行交换的数据(事件),对数据(事件)的处理过程总是沿着序
号逐个递增处理。
Sequence 采用缓存行填充的方式对long类型的一层包装,用以代表事件的序号。
一个 Sequence 用于跟踪标识某个特定的事件处理者( RingBuffffer/Consumer )的处理进度。
虽然一个 AtomicLong 也可以用于标识进度,但定义 Sequence 来负责该问题还有另一个目的,那就是
防止不同的 Sequence 之间的CPU缓存伪共享(Flase Sharing)问题。
另外,Sequence 通过 unsafe 的cas方法从而避免了锁的开销。
Sequencer
Sequencer 是 Disruptor 的真正核心。
生产者与缓存RingBuffffer之间的桥梁、
此接口有两个实现类 SingleProducerSequencer、MultiProducerSequencer ,它们定义在生产者和
消费者之间快速、正确地传递数据的并发算法。
Sequence Barrier
消费者 与 消费者 直接的 隔离 屏障。
消费者 之间,并不是通过 RingBuffffer 进行加锁互斥 隔离,而是 通过 Sequence Barrier 来管理依赖次
序关系, 从而能减少RingBuffffer上的并发冲突;
在一定程度上, Sequence Barrier 类似与 aqs 同步队列
Sequence Barrier 用于保持对 RingBuffffer 的 main published Sequence 和Consumer依赖的其它
Consumer的 Sequence 的引用。
Sequence Barrier 还定义了: Consumer 是否还有可处理的事件的逻辑。
Wait Strategy
定义 Consumer 如何进行等待下一个事件的策略。 (注:Disruptor 定义了多种不同的策略,针对不
同的场景,提供了不一样的性能表现)
Event
在 Disruptor 的语义中,生产者和消费者之间进行交换的数据被称为事件(Event)。
它不是一个被 Disruptor 定义的特定类型,而是由 Disruptor 的使用者定义并指定。
EventProcessor
事件处理器,是消费者线程池Executor的调度单元,
EventProcessor 是对事件业务处理EventHandler与异常处理ExceptionHandler等的一层封装;
EventProcessor 持有特定消费者(Consumer)的 Sequence,并提供事件循环(Event Loop),用于调用
业务事件处理实现EventHandler
EventHandler
Disruptor 定义的事件处理接口,由用户实现,用于处理事件,是 Consumer 的真正实现。
Producer
即生产者,只是泛指调用 Disruptor 发布事件的用户代码,Disruptor 没有定义特定接口或类型
RingBuffffer
基于数组的缓存实现,也是创建sequencer与定义WaitStrategy的入口;
Disruptor
Disruptor的使用入口。
持有RingBuffffer、消费者线程池Executor、消费者仓库ConsumerRepository等引用。
等待策略
- BlockingWaitStrategy
Disruptor的默认策略是BlockingWaitStrategy。在BlockingWaitStrategy内部是使用锁和condition来控制线程的唤醒。BlockingWaitStrategy是最低效的策略,但其对CPU的消耗最小并且在各种不同部署环境中能提供更加一致的性能表现。
- SleepingWaitStrategy
SleepingWaitStrategy 的性能表现跟 BlockingWaitStrategy 差不多,对 CPU 的消耗也类似,但其对生产者线程的影响最小,通过使用LockSupport.parkNanos(1)来实现循环等待。一般来说Linux系统会暂停一个线程约60µs,这样做的好处是,生产线程不需要采取任何其他行动就可以增加适当的计数器,也不需要花费时间信号通知条件变量。但是,在生产者线程和使用者线程之间移动事件的平均延迟会更高。它在不需要低延迟并且对生产线程的影响较小的情况最好。一个常见的用例是异步日志记录。
- YieldingWaitStrategy
YieldingWaitStrategy是可以使用在低延迟系统的策略之一。YieldingWaitStrategy将自旋以等待序列增加到适当的值。在循环体内,将调用Thread.yield(),以允许其他排队的线程运行。在要求极高性能且事件处理线数小于 CPU 逻辑核心数的场景中,推荐使用此策略;例如,CPU开启超线程的特性。
- BusySpinWaitStrategy
性能最好,适合用于低延迟的系统。在要求极高性能且事件处理线程数小于CPU逻辑核心数的场景中,推荐使用此策略;例如,CPU开启超线程的特性。
- PhasedBackoffWaitStrategy
自旋 + yield + 自定义策略,CPU资源紧缺,吞吐量和延迟并不重要的场景。
Disruptor使用案例
消费者:
生产者:
Main方法验证:
Disrurptor 可通过DSL编程实现消费者间任意方式的组合消费:多消费者串行,多消费者并行,多消费者竞争并行。
Disruptor详细介绍相关推荐
- springboo日志t详细介绍
springboot详细介绍 序: 诞生简介 优缺点 springboot日志介绍 1.默认行为 2.自定义日志 3.不适用xml,直接使用yml或者properties配置 yml 配置: prop ...
- HTML页面加载和解析流程详细介绍
浏览器加载和渲染html的顺序.如何加快HTML页面加载速度.HTML页面加载和解析流程等等,在本文将为大家详细介绍下,感兴趣的朋友不要错过 浏览器加载和渲染html的顺序 1. IE下载的顺序是从上 ...
- mysql为什么要压测_mysql集群压测的详细介绍
本篇文章给大家带来的内容是关于mysql集群压测的详细介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. mysql压测 mysql自带就有一个叫mysqlslap的压力测试工具,通 ...
- php比较运算符案列,PHP实例:PHP比较运算符的详细介绍
<PHP实例:PHP比较运算符的详细介绍>要点: 本文介绍了PHP实例:PHP比较运算符的详细介绍,希望对您有用.如果有疑问,可以联系我们. 比拟运算符种类 PHP实战如同它们名称所暗示的 ...
- Tempdb数据库详细介绍
Tempdb数据库详细介绍 一.Tempdb简介 tempdb是SQLServer的系统数据库一直都是SQLServer的重要组成部分,用来存储临时对象.可以简单理解tempdb是SQLServer的 ...
- linux路由介绍,Linux的路由表详细介绍
Linux的路由表详细介绍 一 在Linux下执行route命令[root@localhost backup]# route -nKernel IP routing tableDestination ...
- pythonexcel介绍_Python 中pandas.read_excel详细介绍
Python 中pandas.read_excel详细介绍 #coding:utf-8 import pandas as pd import numpy as np filefullpath = r& ...
- 渡神纪帧数测试软件,渡神纪芬尼斯崛起配置要求高吗 渡神纪配置要求详细介绍_游侠网...
渡神纪芬尼斯崛起配置要求高吗?本作将在12月3日登陆主机和PC,很多玩家比较关心游戏的配置,这里给大家带来了渡神纪配置要求详细介绍,快来了解下吧. 渡神纪配置要求详细介绍 最低要求(720p/30 f ...
- C++11 unordered_map详细介绍
整理的算法模板合集: ACM模板 目录: 1.介绍 1.1 特性 2. 模版 2.1 迭代器 3. 功能函数 3.1 构造函数 3.2 容量操作 3.2.1 size 3.2.2 empty 3.3 ...
最新文章
- 专访郭霖:成长无止境
- 3 个重要因素,带你看透 AI 技术架构方案的可行性
- [unreal4入门系列之八] 使用VS编译UE4编辑器并添加物体到场景中
- dhcp 中继代理配置方法
- 【MongoDB系列】:MongoDB 集群,副本集模式(二)
- 企业办公自动化系统_办公自动化怎么理解?如何实现?
- selenium+ python自动化--断言assertpy
- mysql 失效转移_MySQL基于MHA的FailOver过程
- Spring : @Repository 注解
- swift中的category,扩展
- java驱动刷机_ProductTool(炬力芯片刷机工具+驱动) v5.46 中文安装免费版
- 如何查找专栏(知乎专栏汇总)
- 南京邮电大学c语言实验报告4,南京邮电大学软件设计实验报告..doc
- 学生选课系统功能需求
- python 10进制转2进制
- Rocket 要以硬件隔离引潮流,Docker说” NO”
- 01-死磕QNX someip
- CoreJava基础
- 华为防火墙配置导出(命令行)
- 自学小程序,我教你呀(三)实现大转盘 仿天猫抽奖 跑马灯效果
热门文章
- java 多线程 notifyall_java多线程之 wait(),notify(),notifyAll()
- JavaEE-HTTP协议(一)
- EFT测试78L05损坏分析及解决方案
- Connection closed by foreign host.
- 掌握了c语言就能够编程,C语言编程学习-C语言编程学习app-最火手机站
- 广州大学大学物理练习1 质点运动学
- 病原微生物扩增子数据分析实战(一):bcl2fastq软件完成数据拆分
- 项目需求和客户交流的心得体会
- CPU Utilization is Wrong
- 蓝牙传输速率详细分析【针对蓝牙4.2】