高性能队列disruptor为什么这么快?
背景
Disruptor是LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与I/O操作处于同样的数量级)。基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注。2011年,企业应用软件专家Martin Fowler专门撰写长文介绍。同年它还获得了Oracle官方的Duke大奖。
目前,包括Apache Storm、Camel、Log4j2在内的很多知名项目都应用了Disruptor以获取高性能。
我们先知道disruptor是干什么的,然后笔者带你们源码搞一波,再来看看在log4j2中的运用。
一、Disruptor是什么?
可以这样总结,Disruptor是LMAX开源的、用于替代并发线程间数据交换的环形队列的、基本无锁的(只有部分等待策略存在)、高性能的线程间通讯框架。
Disruptor唯一可能遇到Java锁的时候,就是在消费者等待可用事件进行消费时。而Disruptor为这个等待过程,编写了包括使用锁和不使用锁的多种策略,可根据不同场景和需求进行选择。
开源:https://github.com/LMAX-Exchange/disruptor
二、Disruptor为什么这么快
1、环形队列RingBuffer
一个环形队列,意味着首尾相连,对列可以循环使用,使用数组来保存。环形队列在JVM生命周期中通常是永生的,GC的压力更小。

我们来解释一下这个图:当前有一个consumer,停留在位置12,这时producer假设在位置3,这时producer的下一步是如何处理的呢?producer会尝试读取4,发现下一个可以获取,所以可以安全获取,并且通知一个阻塞的consumer起来活动。如此一直到下一圈11都是安全的(这里我们假设生产者比较快),当producer尝试访问12时发现不能继续,于是自旋等待;当consumer消费时,会调用barrier的waitFor方法,waitFor看到前面最近的安全节点已经到了下一圈的11,于是consumer可以无锁的去消费当前12到下一圈11所有数据,可以想象,这种方式比起synchronized要快上很多倍。
2、弃用锁机制使用CAS
在高度竞争的情况下,锁的性能将超过原子变量的性能,但是更真实的竞争情况下,原子变量的性能将超过锁的性能。同时原子变量不会有死锁等活跃性问题。能不用锁,就不使用锁,如果使用,也要将锁的粒度最小化。
唯一使用锁的就是消费者的等待策略实现类中,下图。补充一句,生产者的等到策略就是LockSupport.parkNanos(1),再自旋判断。
| 名称 | 措施 | 适用场景 |
|---|---|---|
| BlockingWaitStrategy | 加锁 | CPU资源紧缺,吞吐量和延迟并不重要的场景 |
| BusySpinWaitStrategy | 自旋 | 通过不断重试,减少切换线程导致的系统调用,而降低延迟。推荐在线程绑定到固定的CPU的场景下使用 |
| PhasedBackoffWaitStrategy | 自旋 + yield + 自定义策略 | CPU资源紧缺,吞吐量和延迟并不重要的场景 |
| SleepingWaitStrategy | 自旋 + yield + sleep | 性能和CPU资源之间有很好的折中。延迟不均匀 |
| TimeoutBlockingWaitStrategy | 加锁,有超时限制 | CPU资源紧缺,吞吐量和延迟并不重要的场景 |
| YieldingWaitStrategy | 自旋 + yield + 自旋 | 性能和CPU资源之间有很好的折中。延迟比较均匀 |
3、解决伪共享,采用缓存行填充

abstract class SingleProducerSequencerPad extends AbstractSequencer
{
protected long p1, p2, p3, p4, p5, p6, p7; SingleProducerSequencerPad(int bufferSize, WaitStrategy waitStrategy)
{
super(bufferSize, waitStrategy);
}
} public final class SingleProducerSequencer extends SingleProducerSequencerFields
{
protected long p1, p2, p3, p4, p5, p6, p7;
//..省略
}
Java中通过填充缓存行,来解决伪共享问题的思路,现在可能已经是老生常谈,连Java8中都新增了sun.misc.Contended注解来避免伪共享问题。但在Disruptor刚出道那会儿,用缓存行来优化Java数据结构,这恐怕还很新潮。
4、还有一些细节性的
1)通过sequence & (bufferSize - 1)定位元素的index比普通的求余取模(%)要快得多。sequence >>> indexShift 快速计算出sequence/bufferSize的商flag(其实相当于当前sequence在环形跑道上跑了几圈,在数据生产时要设置好flag。
2)合理使用Unsafe,CPU级别指令。实现更加高效地内存管理和原子访问。
至于一些更细节的,下面源码搞起来,还是很简单的。
源码分析:Disruptor源码解读
参考:
https://tech.meituan.com/disruptor.html
https://www.jianshu.com/p/c3c108c3dcfd
高性能队列disruptor为什么这么快?的更多相关文章
- 高性能队列Disruptor系列1--传统队列的不足
在前一篇文章Java中的阻塞队列(BlockingQueue)中介绍了Java中的阻塞队列.从性能上我们能得出一个结论:数组优于链表,CAS优于锁.那么有没有一种队列,通过数组的方式实现,而且采用无锁 ...
- 高性能队列——Disruptor
背景 Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与I/O操作处于同样的数量级).基于Disruptor开发的系统单线程能 ...
- 高性能队列Disruptor系列2--浅析Disruptor
1. Disruptor简单介绍 Disruptor是一个由LMAX开源的Java并发框架.LMAX是一种新型零售金融交易平台,这个系统是建立在 JVM 平台上,核心是一个业务逻辑处理器,它能够在一个 ...
- 高性能队列Disruptor的使用
一.什么是 Disruptor 从功能上来看,Disruptor 是实现了"队列"的功能,而且是一个有界队列.那么它的应用场景自然就是"生产者-消费者"模型的应 ...
- 高性能队列Disruptor系列3--Disruptor的简单使用(译)
简单用法 下面以一个简单的例子来看看Disruptor的用法:生产者发送一个long型的消息,消费者接收消息并打印出来. 首先,我们定义一个Event: public class LongEvent ...
- 高性能无锁队列 Disruptor 初体验
原文地址: haifeiWu和他朋友们的博客 博客地址:www.hchstudio.cn 欢迎转载,转载请注明作者及出处,谢谢! 最近一直在研究队列的一些问题,今天楼主要分享一个高性能的队列 Disr ...
- Disruptor 为什么这么快?
为什么Disruptor不使用队列来实现RingBuffer 队列有两个指针,一个指向队头,一个指向队尾.如果有超过一个生产者想要往队列里放东西,尾指针就将成为一个冲突点,因为有多个线程要更新它. ...
- 从构建分布式秒杀系统聊聊Disruptor高性能队列
前言 秒杀架构持续优化中,基于自身认知不足之处在所难免,也请大家指正,共同进步.文章标题来自码友 简介 LMAX Disruptor是一个高性能的线程间消息库.它源于LMAX对并发性,性能和非阻塞算法 ...
- JUC并发编程与高性能内存队列disruptor实战-下
并发理论 JMM 概述 Java Memory Model缩写为JMM,直译为Java内存模型,定义了一套在多线程读写共享数据时(成员变量.数组)时,对数据的可见性.有序性和原子性的规则和保障:JMM ...
随机推荐
- [bzoj4827][Hnoi2017]礼物_FFT
礼物 bzoj-4827 Hnoi-2017 题目大意:给定两个长度为$n$的手环,第一个手环上的$n$个权值为$x_i$,第二个为$y_i$.现在我可以同时将所有的$x_i$同时加上自然数$c$.我 ...
- 2017-10-02-afternoon
T1 最大值(max) Time Limit:1000ms Memory Limit:128MB 题目描述 LYK有一本书,上面有很多有趣的OI问题.今天LYK看到了这么一道题目: 这里有一个长度 ...
- Binary Tree Preorder Traversal (非递归实现)
具体思路参见:二叉树的非递归遍历(转) 先序遍历(根左右). 即把每一个节点当做根节点来对待. /** * Definition for binary tree * struct TreeNode { ...
- operamasks—omMessageTip的使用
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...
- Linux改动/etc/profile配置错误command is not found自救方法
我的CSDN博客地址: http://blog.csdn.net/caicongyang 博主之前在改动了/etc/profile配置文件方法后,导致bash命令无法用 运行ls命令结果例如以下: - ...
- ×变成x
昨天晚上遇到一个很尴尬的bug. 当使用IE浏览器,跳转链接使用&传参的时候第二个参数是times,也就是×(你有可能看到的是x,实际是×),结果& ...
- C++对象模型——指向Member Function的指针 (Pointer-to-Member Functions)(第四章)
4.4 指向Member Function的指针 (Pointer-to-Member Functions) 取一个nonstatic data member的地址,得到的结果是该member在 cl ...
- C#下JSON字符串的反序列化
C#下JSON字符串的反序列化,一般都是用newtonsoft.json,比较方便..net当然也有提供相应功能,但觉得比较复杂. 所谓反序列化,就是将一个包含JSON内容的字符串,转换回指定对象(不 ...
- mysqld 与 python 邮件监控脚本 内存消耗对比
top - 21:38:40 up 1 day, 10:38, 5 users, load average: 0.00, 0.01, 0.17Tasks: 88 total, 1 running, 8 ...
- C#与excel互操作的错误无法将类型为“Microsoft.Office.Interop.Excel.ApplicationClass”的 COM 对象强制
C#与excel互操作的错误无法将类型为“Microsoft.Office.Interop.Excel.ApplicationClass”的 COM 对象强制 如果您使用的电脑要操作的是office2 ...