JDK的多线程与并发库一文中, 提到了BlockingQueue实现了生产者-消费者模型

BlockingQueue是基于锁实现的, 而锁的效率通常较低. 有没有使用CAS机制实现的生产者-消费者?

Disruptor就是这样.

disruptor使用观察者模式, 主动将消息发送给消费者, 而不是等消费者从队列中取; 在无锁的情况下, 实现queue(环形, RingBuffer)的并发操作, 性能远高于BlockingQueue

1.生产者-消费者

1.1使用Disruptor类

RingBuffer通过Disruptor实例获得

public class Client {

    public static void main(String[] args) throws Exception {

        //1.配置并获得Disruptor
ExecutorService executor = Executors.newCachedThreadPool();
LongEventFactory factory = new LongEventFactory();
// 设置RingBuffer大小, 需为2的N次方(能将求模运算转为位运算提高效率 ), 否则影响性能
int ringBufferSize = 1024 * 1024; //创建disruptor, 泛型参数:传递的事件的类型
// 第一个参数: 产生Event的工厂类, Event封装生成-消费的数据
// 第二个参数: RingBuffer的缓冲区大小
// 第三个参数: 线程池
// 第四个参数: SINGLE单个生产者, MULTI多个生产者
// 第五个参数: WaitStrategy 当消费者阻塞在SequenceBarrier上, 消费者如何等待的策略.
//BlockingWaitStrategy 使用锁和条件变量, 效率较低, 但CPU的消耗最小, 在不同部署环境下性能表现比较一致
//SleepingWaitStrategy 多次循环尝试不成功后, 让出CPU, 等待下次调度; 多次调度后仍不成功, 睡眠纳秒级别的时间再尝试. 平衡了延迟和CPU资源占用, 但延迟不均匀.
//YieldingWaitStrategy 多次循环尝试不成功后, 让出CPU, 等待下次调度. 平衡了延迟和CPU资源占用, 延迟也比较均匀.
//BusySpinWaitStrategy 自旋等待,类似自旋锁. 低延迟但同时对CPU资源的占用也多.
Disruptor<LongEvent> disruptor = new Disruptor<LongEvent>(factory, ringBufferSize, executor, ProducerType.SINGLE, new YieldingWaitStrategy());
// 注册事件消费处理器, 也即消费者. 可传入多个EventHandler ...
disruptor.handleEventsWith(new LongEventHandler());
// 启动
disruptor.start(); //2.将数据装入RingBuffer
RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();
// 创建生产者, 以下方式一使用原始api, 方式二使用新API
//LongEventProducer producer = new LongEventProducer(ringBuffer);
LongEventProducerWithTranslator producer = new LongEventProducerWithTranslator(ringBuffer); ByteBuffer byteBuffer = ByteBuffer.allocate(8); // 这里只是笔者实验, 不是必须要用ByteBuffer保存long数据
for(int i = 0; i < 100; ++i){
byteBuffer.putLong(0, i);
producer.produceData(byteBuffer);
} disruptor.shutdown(); //关闭 disruptor 阻塞直至所有事件都得到处理
executor.shutdown(); // 需关闭 disruptor使用的线程池, 上一步disruptor关闭时不会连带关闭线程池
}
}
// Event封装要传递的数据
public class LongEvent {
private long value;
public long getValue() {
return value;
}
public void setValue(long value) {
this.value = value;
}
}
// 产生Event的工厂
public class LongEventFactory implements EventFactory {
@Override
public Object newInstance() {
return new LongEvent();
}
}
public class LongEventHandler implements EventHandler<LongEvent>  {
// 消费逻辑
@Override
public void onEvent(LongEvent longEvent, long l, boolean b) throws Exception {
System.out.println(longEvent.getValue());
}
}
//生产者实现一
public class LongEventProducer {
// 生产者持有RingBuffer的引用
private final RingBuffer<LongEvent> ringBuffer; public LongEventProducer(RingBuffer<LongEvent> ringBuffer){
this.ringBuffer = ringBuffer;
} public void produceData(ByteBuffer bb){
// 获得下一个Event槽的下标
long sequence = ringBuffer.next();
try {
// 给Event填充数据
LongEvent event = ringBuffer.get(sequence);
event.setValue(bb.getLong(0));
} finally {
// 发布Event, 激活观察者去消费, 将sequence传递给该消费者
//publish应该放在 finally块中以确保一定会被调用->如果某个事件槽被获取但未提交, 将会堵塞后续的publish动作。
ringBuffer.publish(sequence);
}
}
}
//生产者实现二
public class LongEventProducerWithTranslator { // 使用EventTranslator, 封装 获取Event的过程
private static final EventTranslatorOneArg<LongEvent, ByteBuffer> TRANSLATOR = new EventTranslatorOneArg<LongEvent, ByteBuffer>() {
@Override
public void translateTo(LongEvent event, long sequeue, ByteBuffer buffer) {
event.setValue(buffer.getLong(0));
}
}; private final RingBuffer<LongEvent> ringBuffer; public LongEventProducerWithTranslator(RingBuffer<LongEvent> ringBuffer) {
this.ringBuffer = ringBuffer;
} public void produceData(ByteBuffer buffer){
// 发布
ringBuffer.publishEvent(TRANSLATOR, buffer);
}
}

1.2 直接使用RingBuffer

给出了两种方式:EventProcessor与WorkPool(可处理多消费者)

public class ClientForEventProcessor {  

    public static void main(String[] args) throws Exception {
int BUFFER_SIZE = 1024;
int THREAD_NUMBERS = 4; // 这里直接获得RingBuffer. createSingleProducer创建一个单生产者的RingBuffer // 第一个参数为EventFactory,产生数据Trade的工厂类
// 第二个参数是RingBuffer的大小,需为2的N次方
// 第三个参数是WaitStrategy, 消费者阻塞时如何等待生产者放入Event
final RingBuffer<Trade> ringBuffer = RingBuffer.createSingleProducer(new EventFactory<Trade>() {
@Override
public Trade newInstance() {
return new Trade(UUID.randomUUID().toString());
}
}, BUFFER_SIZE, new YieldingWaitStrategy()); //SequenceBarrier, 协调消费者与生产者, 消费者链的先后顺序. 阻塞后面的消费者(没有Event可消费时)
SequenceBarrier sequenceBarrier = ringBuffer.newBarrier(); //创建事件处理器 (消费者): 处理ringBuffer, 用TradeHandler的方法处理(实现EventHandler), 用sequenceBarrier协调生成-消费
//如果存在多个消费者(老api, 可用workpool解决) 那重复执行 创建事件处理器-注册进度-提交消费者的过程, 把其中TradeHandler换成其它消费者类
BatchEventProcessor<Trade> transProcessor = new BatchEventProcessor<Trade>(ringBuffer, sequenceBarrier, new TradeHandler());
//把消费者的消费进度情况注册给RingBuffer结构(生产者) !如果只有一个消费者的情况可以省略
ringBuffer.addGatingSequences(transProcessor.getSequence()); //创建线程池
ExecutorService executors = Executors.newFixedThreadPool(THREAD_NUMBERS);
//把消费者提交到线程池, !说明EventProcessor实现了callable接口
executors.submit(transProcessor); // 生产者, 这里新建线程不是必要的
Future<?> future= executors.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
long seq;
for (int i = 0; i < 10; i++) {
seq = ringBuffer.next();
ringBuffer.get(seq).setPrice(Math.random() * 9999);
ringBuffer.publish(seq);
}
return null;
}
}); Thread.sleep(1000); //等上1秒,等待消费完成
transProcessor.halt(); //通知事件处理器 可以结束了(并不是马上结束!)
executors.shutdown();
}
}
public class ClientForWorkPool {
public static void main(String[] args) throws InterruptedException {
int BUFFER_SIZE = 1024;
int THREAD_NUMBERS = 4; RingBuffer<Trade> ringBuffer = RingBuffer.createSingleProducer(new EventFactory<Trade>() {
public Trade newInstance() {
return new Trade(UUID.randomUUID().toString());
}
}, BUFFER_SIZE); SequenceBarrier sequenceBarrier = ringBuffer.newBarrier(); // 第三个参数: 异常处理器, 这里用ExceptionHandler; 第四个参数WorkHandler的实现类, 可为数组(即传入多个消费者)
WorkerPool<Trade> workerPool = new WorkerPool<Trade>(ringBuffer, sequenceBarrier, new IgnoreExceptionHandler(), new TradeHandler()); ExecutorService executors = Executors.newFixedThreadPool(THREAD_NUMBERS);
workerPool.start(executors); // 生产10个数据
for (int i = 0; i < 8; i++) {
long seq = ringBuffer.next();
ringBuffer.get(seq).setPrice(Math.random() * 9999);
ringBuffer.publish(seq);
} Thread.sleep(1000);
workerPool.halt();
executors.shutdown();
}
}
// 封装交易数据
public class Trade {
private String id; // 订单ID
private String name;
private double price; // 金额 public Trade(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
// 消费者, 这里实现一个接口就行, 写两个是为了同时测试EventProcessor和WorkPool
public class TradeHandler implements EventHandler<Trade>, WorkHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
this.onEvent(event);
}
@Override
public void onEvent(Trade event) throws Exception {
//具体的消费逻辑
System.out.println(event.getId());
}
}

1.3 多生产者-多消费者

一个Event只能被某一个消费者处理

public static void main(String[] args) throws Exception {
//创建RingBuffer
RingBuffer<Order> ringBuffer =
RingBuffer.create(ProducerType.MULTI,
new EventFactory<Order>() {
@Override
public Order newInstance() {
return new Order();
}
},
1024 * 1024, new YieldingWaitStrategy()); SequenceBarrier barriers = ringBuffer.newBarrier(); Consumer[] consumers = new Consumer[3];
for(int i = 0; i < consumers.length; i++){
consumers[i] = new Consumer("ct" + i);
}
// 3个消费者
WorkerPool<Order> workerPool = new WorkerPool<Order>(ringBuffer, barriers, new MyExceptionHandler(), consumers); ringBuffer.addGatingSequences(workerPool.getWorkerSequences());
ExecutorService executors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
workerPool.start(executors);
// 10个生产者, 每个生成者生产20个数据
for (int i = 0; i < 10; i++) {
final Producer p = new Producer(ringBuffer);
new Thread(new Runnable() {
@Override
public void run() {
for(int j = 0; j < 2; j++){
p.produceData(UUID.randomUUID().toString());
}
}
}).start();
} System.out.println("----开始生产----");
Thread.sleep(1000); // 等待消费完成
System.out.println("总共消费数量:" + consumers[0].getCount() ); workerPool.halt();
executors.shutdown();
} static class MyExceptionHandler implements ExceptionHandler {
public void handleEventException(Throwable ex, long sequence, Object event) {}
public void handleOnStartException(Throwable ex) {}
public void handleOnShutdownException(Throwable ex) {}
}
}
public class Consumer implements WorkHandler<Order>{

    private String consumerId;
// 消费计数器
private static AtomicInteger count = new AtomicInteger(0); public Consumer(String consumerId){
this.consumerId = consumerId;
} @Override
public void onEvent(Order order) throws Exception {
System.out.println("当前消费者: " + this.consumerId + ", 消费信息: " + order.getId());
count.incrementAndGet();
} public int getCount(){
return count.get();
}
}
public class Producer {

    private final RingBuffer<Order> ringBuffer;
public Producer(RingBuffer<Order> ringBuffer){
this.ringBuffer = ringBuffer;
}
public void produceData(String data){
long sequence = ringBuffer.next();
try {
Order order = ringBuffer.get(sequence);
order.setId(data);
} finally {
ringBuffer.publish(sequence);
}
}
}
public class Order {
private String id;
private String name;
private double price; public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}

2. 并行处理

除了实现生产者-消费者模型, Disruptor还可以进行多路并行处理(一个Event可以进入多个路径同时进行处理, 因为不同路径操作的是同一个Event, 路径可以汇合)

public class Client {
public static void main(String[] args) throws InterruptedException { long beginTime=System.currentTimeMillis();
int bufferSize=1024;
ExecutorService executor=Executors.newFixedThreadPool(7); // 注意: 线程数>=handler数+1 Disruptor<Trade> disruptor = new Disruptor<Trade>(
new EventFactory<Trade>() {
@Override
public Trade newInstance() {
return new Trade(UUID.randomUUID().toString());
}
}, bufferSize, executor, ProducerType.SINGLE, new BusySpinWaitStrategy());
// 菱形操作
/*
// 创建消费者组(含H1,H2) H1,H2并行执行无先后顺序
EventHandlerGroup<Trade> handlerGroup = disruptor.handleEventsWith(new Handler1(), new Handler2());
// C1,C2都完成后执行C3, 像JMS传递消息
handlerGroup.then(new Handler3());
*/ // 顺序操作
/*
disruptor.handleEventsWith(new Handler1()).handleEventsWith(new Handler2()).handleEventsWith(new Handler3());
*/ // 六边形操作. H1, H4串行执行; H2, H5串行执行; 而H1,H4 与 H2,H5 并行执行
Handler1 h1 = new Handler1();
Handler2 h2 = new Handler2();
Handler3 h3 = new Handler3();
Handler4 h4 = new Handler4();
Handler5 h5 = new Handler5();
disruptor.handleEventsWith(h1, h2);
disruptor.after(h1).handleEventsWith(h4);
disruptor.after(h2).handleEventsWith(h5);
disruptor.after(h4, h5).handleEventsWith(h3); disruptor.start();
// 启动生产线程
executor.submit(new TradePublisher(disruptor));
Thread.sleep(1000); // 等待消费完成 disruptor.shutdown();
executor.shutdown();
System.out.println("总耗时:"+(System.currentTimeMillis()-beginTime));
}
}
public class TradePublisher implements Runnable {

    private Disruptor<Trade> disruptor;
private static final int LOOP = 100;// 模拟百次交易的发生 public TradePublisher(Disruptor<Trade> disruptor) {
this.disruptor = disruptor;
} @Override
public void run() {
TradeEventTranslator tradeTransloator = new TradeEventTranslator();
for (int i = 0; i < LOOP; i++) {
disruptor.publishEvent(tradeTransloator);
}
}
} class TradeEventTranslator implements EventTranslator<Trade> {
private Random random = new Random();
@Override
public void translateTo(Trade event, long sequence) {
this.generateTrade(event);
}
private Trade generateTrade(Trade trade) {
trade.setPrice(random.nextDouble() * 9999);
return trade;
}
}
public class Handler1 implements EventHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch)
throws Exception {
System.out.println("handler1: set name");
event.setName("h1");
}
}
public class Handler2 implements EventHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
System.out.println("handler2: set price");
event.setPrice(17.0);
}
}
public class Handler3 implements EventHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
System.out.println("handler3: name: " + event.getName() + " , price: " + event.getPrice() + "; instance: " + event.getId());
}
}
public class Handler4 implements EventHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
System.out.println("handler4: append name");
event.setName(event.getName() + "h4");
}
}
public class Handler5 implements EventHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
System.out.println("handler5: add price");
event.setPrice(event.getPrice() + 3.0);
}
}

Disruptor快速入门的更多相关文章

  1. 无锁并发框架Disruptor学习入门

    刚刚听说disruptor,大概理一下,只为方便自己理解,文末是一些自己认为比较好的博文,如果有需要的同学可以参考. 本文目标:快速了解Disruptor是什么,主要概念,怎么用 1.Disrupto ...

  2. Disruptor 系列(一)快速入门

    Disruptor 系列(一)快速入门 Disruptor:是一个开源的并发框架,能够在 无锁 的情况下实现网络的 Queue 并发操作,所以处理数据的能力比 Java 本身提供的并发类容器要大的多, ...

  3. Web Api 入门实战 (快速入门+工具使用+不依赖IIS)

    平台之大势何人能挡? 带着你的Net飞奔吧!:http://www.cnblogs.com/dunitian/p/4822808.html 屁话我也就不多说了,什么简介的也省了,直接简单概括+demo ...

  4. SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=》提升)

     SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=>提升,5个Demo贯彻全篇,感兴趣的玩才是真的学) 官方demo:http://www.asp.net/si ...

  5. 前端开发小白必学技能—非关系数据库又像关系数据库的MongoDB快速入门命令(2)

    今天给大家道个歉,没有及时更新MongoDB快速入门的下篇,最近有点小忙,在此向博友们致歉.下面我将简单地说一下mongdb的一些基本命令以及我们日常开发过程中的一些问题.mongodb可以为我们提供 ...

  6. 【第三篇】ASP.NET MVC快速入门之安全策略(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  7. 【番外篇】ASP.NET MVC快速入门之免费jQuery控件库(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  8. Mybatis框架 的快速入门

    MyBatis 简介 什么是 MyBatis? MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架.MyBatis 消除 了几乎所有的 JDBC 代码和参数的手工设置以及结果 ...

  9. grunt快速入门

    快速入门 Grunt和 Grunt 插件是通过 npm 安装并管理的,npm是 Node.js 的包管理器. Grunt 0.4.x 必须配合Node.js >= 0.8.0版本使用.:奇数版本 ...

随机推荐

  1. yii2使用 db log

    在本地测试的时候,输出log,还是输出到db中比较顺手. 配置过程: 1.加入log组件的配置: 'log' =>[ # 追踪级别 # 消息跟踪级别 # 在开发的时候,通常希望看到每个日志消息来 ...

  2. 论文笔记:多标签学习综述(A review on multi-label learning algorithms)

    2014 TKDE(IEEE Transactions on Knowledge and Data Engineering) 张敏灵,周志华 简单介绍 传统监督学习主要是单标签学习,而现实生活中目标样 ...

  3. TOSCA自动化测试工具视频资料

    https://www.udemy.com/ search 'TOCSA' 找到两个免费资料学习

  4. 清除list或者map集合,减少内存的占用率

    1.在编写程序对数据处理的时候我们经常会使用到list和map集合,有些时候我们存到集合里的数据只是暂时性的,在验证完或者使用完之后尽量对list或者map清空,及list.clear()后者map. ...

  5. 【.Net基础二】浅谈引用类型、值类型和装箱、拆箱

    目前在看CLR via C#,把总结的记下来,索性就把他写成一个系列吧. 1.[.Net基础一] 类型.对象.线程栈.托管堆运行时的相互关系 2.[.Net基础二]浅谈引用类型.值类型和装箱.拆箱 引 ...

  6. Docker入门简明教程

    Docker简介 概念 Docker是基于Go语言实现的云开源项目,是基于Linux的多项开源技术提供高效.敏捷和轻量级的容器方案.创建于2013年初.自从开源后就受到了广泛的关注,从长远的眼光来看, ...

  7. Python笔记 #03# Help!

    源:DataCamp datacamp 的 DAILY PRACTICE  + 日常收集. Functions Built-in functions Help! Multiple arguments ...

  8. 关于js中对事件绑定与普通事件的理解

    普通事件指的是可以用来注册的事件: 事件绑定是指把事件注册到具体的元素之上. 通俗点说: 普通事件:给html元素添加一个特定的属性(如:onclick): 事件绑定:js代码中通过标记(id  ta ...

  9. JVM反调调用优化,导致发生大量异常时log4j2线程阻塞

    背景 在使用log4j2打日志时,当发生大量异常时,造成大量线程block问题的问题. 一个关于log4j2的高并发问题:https://blog.fliaping.com/a-high-concur ...

  10. Basic Authentication in ASP.NET Web API

    Basic authentication is defined in RFC 2617, HTTP Authentication: Basic and Digest Access Authentica ...