Disruptor 使用简介
【开发总结】Disruptor 使用简介
在极客时间看到王宝令老师关于 Disruptor 的一篇文章,觉得很有意思。看完之后又在网上找到一些其他关于Disruptor 的资料看了一下。
现在写篇文章总结一下。
使用
Disruptor 百度翻译是干扰者,分裂器的意思。
在这里它其实是一个高性能队列,一个queue。所以我有点想不通为什么名字取成这样。有清楚的同学可以知会我一生。
Disruptor 的使用相对Java集合类中的队列,会更加复杂。
第一步,引入jar包.
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
第二步,生成 Disruptor 对象
第三步,设置队列中消息消费的handler.
第四步,启动 Disruptor 线程。
第五步,获取ringbuffer。生产者通过向 Disruptor 的ringbuffer 来发布消息的。所以事先要先获取ringbuffer。
第六步,发布消息。
/**
* @description:
* @author: lkb
* @create: 2020-10-28 19:46
*/
@Slf4j
public class MyTest {
public static void main(String[] args) {
//指定RingBuffer大小,
//必须是2的N次方
int bufferSize = 1024;
//构建Disruptor
Disruptor<LongEvent> disruptor
= new Disruptor<>(
LongEvent::new,
bufferSize,
DaemonThreadFactory.INSTANCE);
//注册事件处理器
disruptor.handleEventsWith(
(event, sequence, endOfBatch) ->
System.out.println("E: " + event));
//启动Disruptor
disruptor.start();
//获取RingBuffer
RingBuffer<LongEvent> ringBuffer
= disruptor.getRingBuffer();
//生产Event
ByteBuffer bb = ByteBuffer.allocate(8);
for (long l = 0; true; l++) {
bb.putLong(0, l);
//生产者生产消息
ringBuffer.publishEvent(
(event, sequence, buffer) ->
event.set(buffer.getLong(0)), bb);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
}
}
}
}
其中LongEvent 是一个普通的POJO对象
public class LongEvent {
private long value;
public void set(long value) {
this.value = value;
}
}
Disruptor 的使用是典型的生产者-消费者模式。
Java集合中的队列更符合我们对队列的操作习惯。以ArrayBlockingQueue为例,我们可以把ArrayBlockingQueue想象为一个队列管道,生产者线程生产完数据后,将数据丢到队列中,消费者线程从另外一端取出数据,进行消费。
而Disruptor 相对其他传统的队列而言更像一个“大家长”,生成者需要通过这位“大家长”的ringbuffer将消息发送出去,消费者需要将处理操作注册到“大家长”这里。
这样的队列操作不太符合我们的习惯,所以使用上会不那么顺手。
高效的秘诀
在不顺手的情况下,为什么还是有很多系统用到它呢?原因在于它非常高效。

上面是网上找到的性能对比图。可以看到Disruptor性能上是非常高的。
那它是如何实现高效的呢?
- 内存分配更加合理,使用 RingBuffer 数据结构,数组元素在初始化时一次性全部创建,提升缓存命中率;
- 对象循环利用,避免频繁 GC。
- 能够避免伪共享,提升缓存利用率。
- 采用无锁算法,避免频繁加锁、解锁的性能消耗。支持批量消费,消费者可以无锁方式消费多个消息。
对于第四点,相信大家都很清楚。锁操作涉及到操作系统状态切换,这个操作是非常耗时耗资源的。无锁操作可以避免状态切换。
对于前面三点,涉及到一个非常重要的概念,就是缓存。CPU有三级缓存。离CPU越近的缓存,速度越快,但是容量越小。因为CPU的速度远远大于其他硬件的速度,设置缓存能够减小CPU和其他硬件的速度差。这个缓存和生产者消费者中间的队列有异曲同工之妙。
为了提高缓存的命中率,硬件通过局部性原理,在加载一个数据的同时将它周围的数据也加载进去。
程序的局部性原理指的是在一段时间内程序的执行会限定在一个局部范围内。这里的“局部性”可以从两个方面来理解,一个是时间局部性,另一个是空间局部性。时间局部性指的是程序中的某条指令一旦被执行,不久之后这条指令很可能再次被执行;如果某条数据被访问,不久之后这条数据很可能再次被访问。而空间局部性是指某块内存一旦被访问,不久之后这块内存附近的内存也很可能被访问。
上诉的第1、2条,通过将数据设置进连续相邻的内存位置,CPU在读取了一个数据的时候,发现第二个数据已经因为“局部性”原理加载进缓存,就不需要再次去寻址,直接从缓存中获取数据。
第1,2条是对缓存的高效利用,第3条就是对缓存低效使用的规避。
有一种缓存低效使用的方式是“伪共享”。内存是按照缓存行进行管理的。缓存行的大小通常是64个字节。

例如一个缓存行存储了两个对象,对其中一个对象的操作会使得整个缓存行失效。也就是说即使对象B被加入了缓存,但是因为其他对象的操作无效了。
第3条中,Disruptor 中通过将对象包裹,让一个对象充满整个缓存行,避免了伪共享的问题。
还有一点就是,相对于其他阻塞队列,Disruptor 的等待策略更多,功能更加强大。
通过对缓存的利用和无锁操作,Disruptor 成为一个高效队列。
一些思考
Disruptor 的一些思想其实在其他框架上也是常见的。
避免伪共享问题上,MySQL 8.0 版本直接将查询缓存的整块功能删掉了;在高效利用缓存上,线程池、队列等都多算缓存概念的受益者;避免锁操作上,Java的底层的各种锁优化,也是利用这点,比如轻量级锁。
为什么这么多框架会不约而同地想到这些问题呢?
因为计算机、操作系统是非常成熟的,底层都是非常相似的架构。了解计算机底层原理,对这些知识才能触类旁通。所以,啥不说,计算机基础课,我打算再上一遍。
Disruptor 使用简介的更多相关文章
- Disruptor使用简介
disruptor是lmax公司开发的一款java高性能并发框架,其本质是一种类似队列的实现“生产者—消费者 ”模式的组件. 下面是其示例代码: public class DisruptorServe ...
- Java深入学习(6):Disruptor
Disruptor框架简介: 并发框架,基于事件驱动,使用观察者模式 底层采用环形数组,取模算法 简单使用: /** * 声明一个Event:表示生产者和消费者之间传递的数据类型 */ public ...
- Disruptor并发框架(一)简介&上手demo
框架简介 Martin Fowler在自己网站上写了一篇LMAX架构的文章,在文章中他介绍了LMAX是一种新型零售金融交易平台,它能够以很低的延迟产生大量交易.这个系统是建立在JVM平台上,其核心是一 ...
- Disruptor并发框架简介
Martin Fowler在自己网站上写一篇LMAX架构的文章,在文章中他介绍了LMAX是一种新型零售金额交易平台,它能够以很低的延迟产生大量交易.这个系统是建立在JVM平台上,其核心是一个业务逻辑处 ...
- disruptor 高并发编程 简介demo
原文地址:http://www.cnblogs.com/qiaoyihang/p/6479994.html disruptor适用于大规模低延迟的并发场景.可用于读写操作分离.数据缓存,速度匹配(因为 ...
- 从构建分布式秒杀系统聊聊Disruptor高性能队列
前言 秒杀架构持续优化中,基于自身认知不足之处在所难免,也请大家指正,共同进步.文章标题来自码友 简介 LMAX Disruptor是一个高性能的线程间消息库.它源于LMAX对并发性,性能和非阻塞算法 ...
- 无锁并发框架Disruptor学习入门
刚刚听说disruptor,大概理一下,只为方便自己理解,文末是一些自己认为比较好的博文,如果有需要的同学可以参考. 本文目标:快速了解Disruptor是什么,主要概念,怎么用 1.Disrupto ...
- 01-项目简介Springboot简介入门配置项目准备
总体课程主要分为4个阶段课程: ------------------------课程介绍------------------------ 01-项目简介Springboot简介入门配置项目准备02-M ...
- Disruptor 详解 一
这篇博客将主要通过几个示例,简单讲述 Disruptor 的使用方法: 一.disruptor 简介 Disruptor 是英国外汇交易公司 LMAX 开发的一个无锁高性能的线程间消息传递的框架.目前 ...
随机推荐
- shiro-重写标签功能----shiro:hasPermission 标签重写
public abstract class ShiroAuthorizingRealm extends AuthorizingRealm{ private static final String OR ...
- SpringBoot整合MongoDB(实现一个简单缓存)
前言 SpringBoot是常用开发框架,而MongoDB也是最近越来越火的非关系型数据库,这里使用SpringBoot+MongoDB实现一个小案例,当然MongoDB实际做缓存的可能不多,但是这里 ...
- Nginx+Gunicorn+Supervisor部署Flask应用
Flask 内置了简单的 Web 环境,让我们在开发的时候只需要专注于应用实现,而真正要在生产环境运行时这个简单的 Web 环境就不够用了,还需要一系列操作才能让 Web 应用高效的运行起来.现在记录 ...
- Python练习题 019:求分数序列之和
[Python练习题 019] 有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13...求出这个数列的前20项之和. --------------------------------- ...
- 031 01 Android 零基础入门 01 Java基础语法 03 Java运算符 11 运算符的优先级
031 01 Android 零基础入门 01 Java基础语法 03 Java运算符 11 运算符的优先级 本文知识点:Java中运算符的优先级 运算符的优先级问题 前面学习了很多的运算符,如果这些 ...
- 004 01 Android 零基础入门 01 Java基础语法 01 Java初识 04 Java程序的结构
004 01 Android 零基础入门 01 Java基础语法 01 Java初识 04 Java程序的结构 Java程序的结构 Java程序外层--类 程序外层,如下面的代码,是一个类的定义. c ...
- SQL数据库删除和还原的时候提示 被占用 记录一下
设置离线 1)ALTER DATABASE [datebase] SET OFFLINE WITH ROLLBACK IMMEDIATE设置在线 2)ALTER DATABASE [datebase] ...
- 从实体框架核心开始:构建一个ASP。NET Core应用程序与Web API和代码优先开发
下载StudentApplication.Web.zip - 599.5 KB 下载StudentApplication.API.zip - 11.5 KB 介绍 在上一篇文章中,我们了解了实体框架的 ...
- shell-的变量-局部变量
1. 定义本地变量 本地变量在用户当前的shell生产期的脚本中使用.例如,本地变量OLDBOY取值为ett098,这个值只在用户当前shell生存期中有意义.如果在shell中启动另一个进程或退出, ...
- DM9000裸机驱动程序设计
对于任何一个硬件模块的设计,首先第一步都是要先了解硬件本身后,再开始程序的软件设计.而由于DM9000的芯片文档内容很多,要驱动好网卡,需要很长时间,特别对于新手比较困难,所以可以参考linux内核代 ...