Disruptor 是一款高性能的有界内存队列,目前应用非常广泛,Log4j2、SpringMessaging、HBase、Storm 都用到了 Disruptor,那 Disruptor 的性能为什么这么高呢?Disruptor 项目团队曾经写过一篇论文,详细解释了其原因,可以总结为如下:
- 内存分配更加合理,使用 RingBuffer 数据结构,数组元素在初始化时一次性全部创建,提升缓存命中率;对象循环利用,避免频繁 GC。
- 能够避免伪共享,提升缓存利用率。3. 采用无锁算法,避免频繁加锁、解锁的性能消耗。
- 支持批量消费,消费者可以无锁方式消费多个消息。
其中,前三点涉及到的知识比较多,所以今天咱们重点讲解前三点,不过在详细介绍这些知识之前,我们先来聊聊 Disruptor 如何使用,好让你先对 Disruptor 有个感官的认识。
下面的代码出自官方示例,我略做了一些修改,相较而言,Disruptor 的使用比 Java SDK提供 BlockingQueue 要复杂一些,但是总体思路还是一致的,其大致情况如下:
- 在 Disruptor 中,生产者生产的对象(也就是消费者消费的对象)称为 Event,使用Disruptor 必须自定义 Event,例如示例代码的自定义 Event 是 LongEvent;
- 构建 Disruptor 对象除了要指定队列大小外,还需要传入一个 EventFactory,示例代码中传入的是LongEvent::new;
- 消费 Disruptor 中的 Event 需要通过 handleEventsWith() 方法注册一个事件处理器,发布 Event 则需要通过 publishEvent() 方法。
/* 自定义 Event */
class LongEvent {
private long value;
public void set( long value )
{
this.value = value;
}
}
/* 指定 RingBuffer 大小, 9 // 必须是 2 的 N 次方 */
int bufferSize = 1024;
/* 构建 Disruptor */
Disruptor<LongEvent> disruptor
= new Disruptor<>(
LongEvent: : new,
bufferSize,
DaemonThreadFactory.INSTANCE );
/* 注册事件处理器 */
disruptor.handleEventsWith(
(event, sequence, endOfBatch) - >
System.out.println( "E: " + event ) );
/* 启动 Disruptor */
disruptor.start();
/* 获取 RingBuffer */
RingBuffer<LongEvent> ringBuffer
= disruptor.getRingBuffer();
/* 生产 Event */
ByteBuffer bb = ByteBuffer.allocate( 8 );
for ( long l = 0; true; l++ )
{
bb.put