一. Disruptor 介绍
> Disruptor是英国LMAX 公司开源的,Java实现的用于线程间通信的消息组件; 号称能在一个线程里每秒处理6百万订单。
二. Disruptor 特点
> 底层数据结构为数组,预分配内存大小,减少gc次数;内存地址连续性,利于缓存命中。
> 队列大小为2^n,通过位运算,加快定位的速度;下标采取递增的形式,long类型,足够大。
> 相对于传统并发队列,Disruptor只有头指针,而且锁是乐观锁(cas),在标准Disruptor应用中,只有一个生产者,避免了头指针锁的争用,所以视为无锁队列。
三.主要组件
四.Sequence 序列号
> 基本属性字段填充+sequence本身=64byte,正好一个缓存行,避免了cpu缓存伪共享,避免锁竞争
> Volatile 保证了内存可见性
> Volatile无法保证原子操作,利用cas 操作保证生产端原子操作,乐观锁
五.Ringbuffer 环形队列
> 数据缓冲区,不同线程之间传递数据的buffer。RingBuffer是存储消息的地方,通过一个名为cursor的Sequence对象指示队列的头,协调多个生产者向RingBuffer中添加消息,并用于在消费者端判断RingBuffer是否为空。
>队列尾的Sequence并没有在RingBuffer中,而是由消费者维护。这样的好处是多个消费者处理消息的方式更加灵活,可以在一个RingBuffer上实现消息的单播,多播,流水线以及它们的组合。
>在RingBuffer中维护了一个名为gatingSequences的Sequence数组来跟踪相关Seqence。
六.生产端/消费端
七.消费策略
当消费者等待在SequenceBarrier上时,有许多可选的等待策略,不同的等待策略在延迟和CPU资源的占用上有所不同,可以视应用场景选择:
BusySpinWaitStrategy : 自旋等待,类似Linux Kernel使用的自旋锁。低延迟但同时对CPU资源的占用也多。
BlockingWaitStrategy : 使用锁和条件变量。CPU资源的占用少,延迟大。
SleepingWaitStrategy : 在多次循环尝试不成功后,选择让出CPU,等待下次调度,多次调度后仍不成功,尝试前睡眠一个纳秒级别的时间再尝试。这种策略平衡了延迟和CPU资源占用,但延迟不均匀。
YieldingWaitStrategy : 在多次循环尝试不成功后,选择让出CPU,等待下次调。平衡了延迟和CPU资源占用,但延迟也比较均匀。
PhasedBackoffWaitStrategy : 上面多种策略的综合,CPU资源的占用少,延迟大
八.总结
> 性能上数组优于链表,CAS优于锁
>消费端尽量避免I/O操作,避免消息积压
>选用单生产者模式,避免锁竞争和减少内存开销