linux无锁队列性能对比,高性能无锁队列:disruptor

66b52468c121889b900d4956032f1009.png

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

1. 简要描述

disruptor是一个高性能的队列,最初是应用在LMAX架构中。我们可以把disruptor用在生产者-消费者问题以获取高效地处理和高吞吐量。现在disruptor版本已稳定在3.0,请尽量使用版本3.0,因为低版本的api和3.0几乎不兼容,并且版本3.0提供更多生产者-消费者设计

支持。

2. 我们不一样:阻塞队列 vs Disruptor

(稳) 稳健派:BlockingQueue

阻塞队列是一个稳健派的设计:阻塞队列是一个FIFO队列,生产者(Producer)往队列里发布(publish)一件物料时,消费者(Consumer)能获得通知;如果没有物料时,消费者被堵塞,直到生产者发布了新的物料,防止生产者生产速度过快,导致内存溢出(OOM)。

平常我们实现生产者-消费者模型时,一般会选用阻塞队列ArrayBlockingQueue来实现。当数据量上升到模型无法处理时,这时阻塞队列的“稳健”操作开始带来了负面影响:

锁是慢的

~ > 加锁时:线程会因为获取不到锁而被阻塞挂起;

~ > 锁释放时:阻塞的线程会被唤醒;而整个过程具有大量的时间消耗。

在disruptor论文中讲述一个实验:测试程序中调用了一个函数,该函数会对一个64位计数器循环自增5亿次;方式耗时(ms)单线程无锁300

单线程加锁10000

两个线程加锁224000

从实验中可以看出并发环境下锁保证了线程安全,却带来了糟糕的性能表现。

伪共享问题

9616e1e01f373b4ef47db8e8b43c5c6f.png

(快) 别人家的孩子:Disruptor

Disruptor不仅同样能完成阻塞队列的功能,而且Disruptor还能做的更多。

disruptor:无锁设计,通过CPU指令级别的CAS操作保证线程安全,同时大大提高处理速度;

缓冲行填充解决伪共享问题;

提供了良好的并发编程接口,适应多种场景:消费者既可以并行处理,也可以消费同一个,还可以相互依赖消费;

底层的极度优化:缓冲行解决伪共享问题、预先分配内存空间等。

dfef9acce027c4ed101b9319c0a0b929.png

disruptor使用RingBuffer实现,而RingBuffer相比于链表的优势有:

(1)RingBuffer使用数组实现,访问比链表快,并且由于CPU缓冲行会加载相邻数据的特性,数组的元素在硬件级别是会被预加载的。但同时由于CPU缓冲行的这个特性从而导致了伪共享问题,不过disruptor使用了缓冲行填充技术解决伪共享。

(2)RingBuffer可以预先分配堆内存空间,数组元素保证一直有效。当生产者生产物料,只需要拿到预先创建好的RingBuffer元素实例直接进行设置,从而减少gc的产生。

ArrayBlockingQueue vs Disruptor

在disruptor官方说明文档中,它做了一个对比实验:生产者生产物料后等待1微秒,然后消费者消费物料;重复执行5000万次

CPU : 2.2Ghz Core i7-2720QM

OS: Ubuntu 11.04指标Array Blocking Queue (ns)Disruptor (ns)平均延迟32,75752

99%的观测值低于2,097,152128

99.99%的观测值低于4,194,3048,192

3. 该如何使用你,我的disruptor?

这里简单介绍:如何使用disruptor实现Single-Producer ~ Multi-Consumer模型

模型实现自定义Event,作为生产者-消费者模型中的物料;同时实现EventFactory>接口,作为物料的生成工具。

自定义生产者模型,批量从数据源获取”原料”组装放入物料中。

自定义消费者模型,实现WorkHandler>接口,作为自定义Event的处理器。

Disruptor采用WorkPool的消费模式,保证消费者不会重复消费物料。

部分实现代码// 定义物料

public class BatchIdEvent {

private List batch;

public BatchIdEvent() { this.batch = new ArrayList<>(); }

public List getBatch() { return batch; }

public void setBatch(List batch) { this.batch = batch; }

public static final EventFactory EVENT_FACTORY = () -> new BatchIdEvent();

}

// 自定义生产者模型

class DisruptorProducer {

...

public void onData(List batchIds) {

long sequence = ringBuffer.next();

BatchIdEvent event = ringBuffer.get(sequence);

event.setBatch(batchIds);

ringBuffer.publish(sequence);

}

}

// 实现WorkHandler> 接口,实现消费者模型

class EventConsumer implements WorkHandler {

...

@Override

public void onEvent(BatchIdEvent batchIdEvent) throws Exception {

doJob(batchIdEvent.getBatch());

...

}

}

// 使用WorkPool的消费方式,并启动disruptor

for (int i = 0; i < consumerNum; i++) {

AtomicInteger count = new AtomicInteger(0);

eventConsumers[i] = new EventConsumer(count);

}

disruptor.handleEventsWithWorkerPool(eventConsumers);

disruptor.start();

4. 推荐阅读

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值