并发编程框架Disruptor之高性能设计
时间:2022-07-26
本文章向大家介绍并发编程框架Disruptor之高性能设计,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
文章收录在我的 GitHub 仓库,欢迎Star/fork:
Java-Interview-Tutorial https://github.com/Wasabi1234/Java-Interview-Tutorial
架构 UML
1 单线程写
Disruptor的RingBuffer, 之所以可以做到完全无锁,也是因为"单线程写",这是所有"前提的前提",离了这个前提条件,没有任何技术可以做到完全无锁。Redis、Netty等等高性能技术框架的设计都是这个核心思想。
2 系统内存优化-内存屏障
要正确的实现无锁,还需要另外一个关键技术:内存屏障。对应到Java语言,就是valotile变量与happens before语义。
参阅: 内存屏障 - Linux的smpwmb()/smp rmb() 系统内核:比如Linux的kfifo:smp wmb(),无论是底层的读写 都是使用了Linux的smp wmb
https://github.com/opennetworklinux/linux-3.8.13/blob/master/kernel/kfifo.c
3 系统缓存优化-消除伪共享
缓存系统中是以缓存行(cache line) 为单位存储的。缓存行是2的整数幂个连续字节,一般为32-256个字节。最常见的缓存行大小是64个字节。
当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享。
核心:Sequence
可看成是一个AtomicLong用于标识进度。还有另外一个目的就是防止不同Sequence之间CPU缓存伪共享(Flase Sharing)的问题。
- 如下设计保证我们保存的 value 永远在一个缓存行中。(8 个long,正好 64 字节)。这也是一个空间换时间的案例。 4 算法优化-序号栅栏机制我们在生产者进行投递Event的时候,总是会使用:
long sequence = ringBuffer.next();
Disruptor3.0中,序号栅栏SequenceBarrier和序号Sequence搭配使用。协调和管理消费者与生产者的工作节奏,避免了锁和CAS的使用。
- 消费者序号数值必须小于生产者序号数值
- 消费者序号数值必须小于其前置(依赖关系)消费者的序号数值
- 生产者序号数值不能大于消费者中最小的序号数值
- 以避免生产者速度过快,将还未来得及消费的消息覆盖
SingleProducerSequencerPad#next
/**
* @see Sequencer#next(int)
*/
@Override
public long next(int n) // 1
{
if (n < 1) // 初始值:sequence = -1
{
throw new IllegalArgumentException("n must be > 0");
}
// 语义级别的
// nextValue为SingleProducerSequencer的变量
long nextValue = this.nextValue;
long nextSequence = nextValue + n;
// 用于判断当前序号是否绕过整个 ringbuffer 容器
long wrapPoint = nextSequence - bufferSize;
// 用于缓存优化
long cachedGatingSequence = this.cachedValue;
if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue)
{
long minSequence;
while (wrapPoint > (minSequence = Util.getMinimumSequence(gatingSequences, nextValue)))
{
LockSupport.parkNanos(1L); // TODO: Use waitStrategy to spin?
}
this.cachedValue = minSequence;
}
this.nextValue = nextSequence;
return nextSequence;
}
参考
- django中间件Middleware
- springboot kafka集成(实现producer和consumer)
- 分布式锁的实现(redis)
- springboot scheduled并发配置
- RabbitMQ高可用集群配置
- zookeeper curator处理会话过期session expired
- redis事务
- 数据库表反向生成(一) MyBatis-generator与IDEA的集成
- 数据库表反向生成(二) Django ORM inspectdb
- RabbitMQ与AMQP协议
- 大数据算法设计模式(2) - 左外链接(leftOuterJoin) spark实现
- hs_err_pid
- django celery的分布式异步之路(二) 高并发
- django celery的分布式异步之路(一) 起步
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 关于vue的title标签中出现的htmlWebpackPlugin.options.title
- dotnet tool 工具安装提示 Could not find a part of the path 安装失败
- dotnet core 进行 XML 序列化抛出 XmlSerializers dll 文件找不到
- C# dotnet 高性能多线程工具 ExecuteOnceAwaiter 只执行一次的任务
- 一道Postgresql递归树题
- 突击并发编程JUC系列-JDK1.8 扩展类型 LongAdder
- 利用tensorflow训练简单的生成对抗网络GAN
- 《Java从入门到失业》第三章:基础语法及基本程序结构(3.7):运算符(基本算数运算符、原码、反码、补码)
- 《Java从入门到失业》第三章:基础语法及基本程序结构(3.6):基本数据类型及字符集编码(字符编码和char型)
- 《Java从入门到失业》第三章:基础语法及基本程序结构(3.6):基本数据类型及字符集编码(整型、浮点型、布尔型)
- 多图详解Spring框架的设计理念与设计模式
- 《Java从入门到失业》第三章:基础语法及基本程序结构(3.2-3.5):标识符、关键字、注释、变量及常量
- 《Java从入门到失业》第三章:基础语法及基本程序结构(3.1):一个简单的例子
- 详解Java解析XML的四种方法
- 《Java从入门到失业》第四章:类和对象(4.6):类路径