将系统性能优化到极致,永远是程序爱好者所努力的一个方向。在java并发领域,也有很多的实践与创新,小到乐观锁、CAS,大到netty线程模型、纤程Quasar、kilim等。Disruptor是一个轻量的高性能并发框架,以惊人的吞吐量而受到广泛的关注。Disruptor为提高程序的并发性能,提供了很多新的思路,比如:
缓存行填充,消除伪共享;
RingBuffer无锁队列设计;
预分配缓存对象,使用缓存的循环覆盖取代缓存的新增删除等;
下文将从源码角度解析Disruptor的实现原理。
1 Disruptor术语
Disruptor有很多自身的概念,使得初学者看代码会比较费劲。因此在深入Disruptor原理之前,需要先了解一下Disruptor主要的几个核心类或接口。
Sequence: 采用缓存行填充的方式对long类型的一层包装,用以代表事件的序号。通过unsafe的cas方法从而避免了锁的开销;
Sequencer: 生产者与缓存RingBuffer之间的桥梁。单生产者与多生产者分别对应于两个实现SingleProducerSequencer与MultiProducerSequencer。Sequencer用于向RingBuffer申请空间,使用publish方法通过waitStrategy通知所有在等待可消费事件的SequenceBarrier;
WaitStrategy: WaitStrategy有多种实现,用以表示当无可消费事件时,消费者的等待策略;
SequenceBarrier: 消费者与缓存RingBuffer之间的桥梁。消费者并不直接访问RingBuffer,从而能减少RingBuffer上的并发冲突;
EventProcessor: 事件处理器,是消费者线程池Executor的调度单元,是对事件处理EventHandler与异常处理ExceptionHandler等的一层封装;
Event: 消费事件。Event的具体实现由用户定义;
RingBuffer: 基于数组的缓存实现,也是创建sequencer与定义WaitStrategy的入口;
Disruptor: Disruptor的使用入口。持有RingBuffer、消费者线程池Executor、消费者集合ConsumerRepository等引用。
2 Disruptor源码分析
2.1 Disruptor并发模型
并发领域的一个典型场景是生产者消费者模型,常规方式是使用queue作为生产者线程与消费者线程之间共享数据的方法,对于queue的读写避免不了读写锁的竞争。Disruptor使用环形缓冲区RingBuffer作为共享数据的媒介。生产者通过Sequencer控制RingBuffer,以及唤醒等待事件的消费者,消费者通过SequenceBarrier监听RingBuffer的可消费事件。考虑一个场景,一个生产者A与三个消费者B、C、D,同时D的事件处理需要B与C先完成。则该模型结构如下:
在这个结构下,每个消费者拥有各自独立的事件序号Sequence,消费者之间不存在共享竞态。SequenceBarrier1监听RingBuffer的序号cursor,消费者B与C通过SequenceBarrier1等待可消费事件。SequenceBarrier2除了监听cursor,同时也监听B与C的序号Sequence,从而将最小的序号返回给消费者D,由此实现了D依赖B与C的逻辑。
RingBuffer是Disruptor高性能的一个亮点。RingBuffer就是一个大数组,事件以循环覆盖的方式写入。与常规RingBuffer拥有2个首尾指针的方式不同,D