Disruptor(无锁并发框架)-发布

简介:

假如你生活在另外一个星球,我们最近开源了一套高性能的基于消息传递的开源框架。

下面我给大家介绍一下如何将消息通过Ring buffer在无锁的情况下进行处理。

在深入介绍之前,可以先快速阅读一下Trish发表的文章,该文章介绍了ring buffer和其工作原理。

这篇文章的要点如下:

1.ring buffer是由一个大数组组成的。

2.所有ring buffer的“指针”(也称为序列或游标)是java long类型的(64位有符号数),指针采用往上计数自增的方式。(不用担心越界,即使每秒1,000,000条消息,也要消耗300,000年才可以用完)。

3.对ring buffer中的指针进行按ring buffer的size取模找出数组的下标来定位入口(类似于HashMap的entry)。为了提高性能,我们通常将ring buffer的size大小设置成实际使用的2倍。

这样我们可以通过位运算(bit-mask )的方式计算出数组的下标。

Ring buffer的基础结构

注意:和代码中的实际实现,我这里描述的内容是进行了简化和抽象的。从概念上讲,我认为更加方面理解。

ring buffer维护两个指针,“next”和“cursor”。

basic-structure1

在上面的图示里,是一个size为7的ring buffer(你应该知道这个手工绘制的图示的原理),从0-2的坐标位置是填充了数据的。

“next”指针指向第一个未填充数据的区块。“cursor”指针指向最后一个填充了数据的区块。在一个空闲的ring bufer中,它们是彼此紧邻的,如上图所示。

填充数据(Claiming a slot,获取区块)

Disruptor API 提供了事务操作的支持。当从ring buffer获取到区块,先是往区块中写入数据,然后再进行提交的操作。

假设有一个线程负责将字母“D”写进ring buffer中。将会从ring buffer中获取一个区块(slot),这个操作是一个基于CAS的“get-and-increment”操作,将“next”指针进行自增。这样, 当前线程(我们可以叫做线程D)进行了get-and-increment操作后,

指向了位置4,然后返回3。这样,线程D就获得了位置3的操作权限。

after-d-claim2

接着,另一个线程E做类似以上的操作。

after-e-claim3

提交写入

以上,线程D和线程E都可以同时线程安全的往各自负责的区块(或位置,slots)写入数据。但是,我们可以讨论一下线程E先完成任务的场景…

线程E尝试提交写入数据。在一个繁忙的循环中有若干的CAS提交操作。线程E持有位置4,它将会做一个CAS的waiting操作,直到  “cursor”变成3,然后将“cursor”变成4。

再次强调,这是一个原子性的操作。因此,现在的ring buffer中,“cursor”现在是2,线程E将会进入长期等待并重试操作,直到 “cursor”变成3。

然后,线程D开始提交。线程E用CAS操作将“cursor”设置为3(线程E持有的区块位置)当且仅当“cursor”位置是2.“cursor”当前是2,所以CAS操作成功和提交也成功了。

这时候,“cursor”已经更新成3,然后所有和3相关的数据变成可读。

这是一个关键点。知道ring buffer填充了多少 – 即写了多少数据,那一个序列数写入最高等等,是游标的一些简单的功能。“next”指针是为了保证写入的事务特性。

after-d-commits4

最后的疑惑是线程E的写入可见,线程E一直重试,尝试将“cursor”从3更新成4,经过线程D操作后已经更新成3,那么下一次重试就可以成功了。

after-e-commits5

总结

写入数据可见的先后顺序是由线程所抢占的位置的先后顺序决定的,而不是由它的提交先后决定的。但你可以想象这些线程从网络层中获取消息,这是和消息按照时间到达的先后顺序是没什么不同的,而两个线程竞争获取一个不同循序的位置。

因此,这是一个简单而优雅的算法,写操作是原子的,事务性和无锁,即使有多个写入线程。


文章转自 并发编程网-ifeve.com

相关文章
|
6月前
|
存储 安全 Java
【无锁并发框架Disruptor】
【无锁并发框架Disruptor】
|
3月前
|
存储 消息中间件 缓存
并发编程 - 通过 Disruptor 来实现无锁无阻塞的并发编程
并发编程 - 通过 Disruptor 来实现无锁无阻塞的并发编程
71 1
|
5月前
|
安全 前端开发 程序员
C++11 并发编程基础(一):并发、并行与C++多线程
C++11标准在标准库中为多线程提供了组件,这意味着使用C++编写与平台无关的多线程程序成为可能,而C++程序的可移植性也得到了有力的保证。另外,并发编程可提高应用的性能,这对对性能锱铢必较的C++程序员来说是值得关注的。
110 0
C++11 并发编程基础(一):并发、并行与C++多线程
|
6月前
|
存储 缓存 算法
异步编程 - 13 高性能线程间消息传递库 Disruptor
异步编程 - 13 高性能线程间消息传递库 Disruptor
54 0
|
8月前
|
缓存 Java 容器
【并发技术10】线程并发库的使用
【并发技术10】线程并发库的使用
高性能无锁并发框架Disruptor,太强了
Disruptor是一个开源框架,研发的初衷是为了解决高并发下队列锁的问题,最早由LMAX提出并使用,能够在无锁的情况下实现队列的并发操作,并号称能够在一个线程里每秒处理6百万笔订单
|
缓存 安全 算法
高性能无锁并发框架Disruptor,太强了!
Disruptor是一个开源框架,研发的初衷是为了解决高并发下队列锁的问题,最早由LMAX提出并使用,能够在无锁的情况下实现队列的并发操作,并号称能够在一个线程里每秒处理6百万笔订单官网:lmax-exchange.github.io/disruptor/目前,包括Apache Storm、Camel、Log4j2在内的很多知名项目都应用了Disruptor以获取高性能为什么会产生Disruptor框架「目前Java内置队列保证线程安全的方式:」ArrayBlockingQueue:基于数组形式的队列,通过加锁的方式,来保证多线程情况下数据的安全;LinkedBlockingQue基于链表形式
|
缓存 安全 Java
Java多线程-Disruptor性能用测
Java多线程框架Disruptor
273 1
Java多线程-Disruptor性能用测
|
缓存 Java
JAVA线程及简单同步实现的原理解析
JAVA线程及简单同步实现的原理解析线程一、内容简介:  本文主要讲述计算机中有关线程的相关内容,以及JAVA中关于线程的基础知识点,为以后的深入学习做铺垫。如果你已经是高手了,那么这篇文章并不适合你。
1302 0
|
Java 异构计算
Java并发编程之概念一:并行与并发
Java并发编程之概念一:并行与并发概念解释并行性和并发性是既相似又有区别的两个概念。 并行性是指两个或多个事件在同一时刻发生。 而并发性是指连个或多个事件在同一时间间隔内发生。在多道程序环境下,并发性是指在一段时间内宏观上有多个程序在同时运行,但在单处理机环境下(一个处理器),每一时刻却仅能有一道程序执行,故微观上这些程序只能是分时地交替执行。
2247 0