为了账号安全,请及时绑定邮箱和手机立即绑定

并发编程框架----disruptor框架(二)

标签:
Java 架构

一 从上一篇博客可以知道disruptor的工作图如下:

https://img1.sycdn.imooc.com//5b781553000116d109400404.jpg


给人的印象就是RingBuffer是其核心,生产者向RingBuffer中写入元素,消费者从RingBuffer中消费元素。

        RingBuffer其实就是一个环,首尾连接的一个环,只维护一个next()指向的下一个元素,当数据超过最大的限制的时候,其实就是一个覆盖操作,打个比方现在RingBuffer的长度为2^3=8,将下标从0到7的索引填满之后,下一个索引应该是8,这个时候其实只需要进行简单的取模操作即可,8%8就是覆盖第0个元素对象的数据,12的话就是12%8=4,就覆写索引下标为4的位置。

       之前有说RingBuffer中槽的大小需要是2^n,这样是因为假如是2的n次方的话,进行操作完全可以用位运算,这是速度非常快的,比如: 8 >> 1 = 4   8 << 1 = 16  


二 示例

下面以一个例子来模拟实现并行计算500个一亿求和的过程。

SingleSumEvent.java  这个类用来存储每个线程计算的值。

package com.lxj.disruptor2;public class SingleSumEvent {	private String thread;	
	private long value;	public String getThread() {		return thread;
	}	public void setThread(String thread) {		this.thread = thread;
	}	public long getValue() {		return value;
	}	public void setValue(long value) {		this.value = value;
	}	
	
	public void calculate(long start , long end) {		for(long i = start ; i <= end ; i++) {
			value += i;
		}
	}	@Override
	public String toString() {		return "SingleSumEvent [thread=" + thread + ", value=" + value + "]";
	}

	
}


MergerSumEvent.java  用来把每个生产者计算出来的值进行累加

package com.lxj.disruptor2;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

import com.lmax.disruptor.WorkHandler;

public class MergerSumEvent implements WorkHandler<SingleSumEvent>{

    //数值太大Long型会溢出
	private volatile BigInteger sum = new BigInteger("0");
	
    //原子操作
	private volatile AtomicInteger count = new AtomicInteger(1);
	
    //闭锁
	private volatile CountDownLatch latch ;
	
	
	public MergerSumEvent(CountDownLatch latch) {
		this.latch = latch;
	}

    //每个SingleSumEvent 线程计算出来的值交给MergerSumEvent来累加 
	@Override
	public  void onEvent(SingleSumEvent singleSumEvent) throws Exception {
		 sum = sum.add(new BigInteger(singleSumEvent.getValue()+""));
		 System.out.println("线程"+singleSumEvent.getThread() +"  " + count + "个一亿求和的结果为:  " + sum );
		 count.incrementAndGet();
		 if(count.intValue() == 500) {
			 latch.countDown();
		 }
	}

	
	public BigInteger getSum() {
		return sum;
	}
}


SumEventProducer.java   模拟外部磁盘数据或者网络传输过来的数据,并进行发布,是一个事件源,每次都触发,自己受到调用

package com.lxj.disruptor2;import com.lmax.disruptor.RingBuffer;public class SumEventProducer {	private final RingBuffer<SingleSumEvent> ringBuffer;	
	public SumEventProducer(RingBuffer<SingleSumEvent> ringBuffer) {	     this.ringBuffer = ringBuffer;
	}	public void onData(long sum) {		long sequence = ringBuffer.next();		try {
			SingleSumEvent singleSumEvent = ringBuffer.get(sequence);
			singleSumEvent.calculate(1, sum);
			singleSumEvent.setValue(singleSumEvent.getValue());
			singleSumEvent.setThread(Thread.currentThread().getName()); 
		} finally {			//发布事件
			ringBuffer.publish(sequence);
		}
	}
	
	

}


SumEventMain.java

package com.lxj.disruptor2;

import java.time.Duration;
import java.time.Instant;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import com.lmax.disruptor.AlertException;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.ExceptionHandler;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.Sequence;
import com.lmax.disruptor.SequenceBarrier;
import com.lmax.disruptor.TimeoutException;
import com.lmax.disruptor.WaitStrategy;
import com.lmax.disruptor.WorkerPool;
import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;


public class SumEventMain {

	public static void main(String[] args) throws Exception {
		// 获取RungBuffer: ProducerType.MULTI: 多个生产者
		RingBuffer<SingleSumEvent> ringBuffer = RingBuffer.create(ProducerType.MULTI, SingleSumEvent::new, (int) (Math.pow(2, 20)), new YieldingWaitStrategy());		
		
		SequenceBarrier sequenceBarrier = ringBuffer.newBarrier();

		final CountDownLatch latch = new CountDownLatch(1);
		
		// 定义一个用于整合计算的消费者
		MergerSumEvent mergerSumEvent = new MergerSumEvent(latch);

		WorkerPool<SingleSumEvent> workerPool = new WorkerPool<SingleSumEvent>(ringBuffer, sequenceBarrier,
				new IntEventExceptionHandler(), mergerSumEvent);

		ringBuffer.addGatingSequences(workerPool.getWorkerSequences());
		workerPool.start(Executors.newCachedThreadPool());

		final SumEventProducer p = new SumEventProducer(ringBuffer);
		Instant start = Instant.now();
		for(int i = 0 ; i < 100; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					for (int j = 0; j < 5; j++) {
						p.onData(100000000L);
					}
				}
			}).start();
		}
		//等待所有任务完成
		latch.await();
		Instant end = Instant.now();
		System.out.println("500个一亿(500*100000000)总共花费的时间: "+ Duration.between(start,end).toMillis() + " 秒,值为:   " +mergerSumEvent.getSum());
		workerPool.halt(); //通知事件(或者说消息)处理器 可以结束了(并不是马上结束!!!)  
	}

 
	
	
	static class IntEventExceptionHandler implements ExceptionHandler<Object> {
		public void handleEventException(Throwable ex, long sequence, Object event) {
		}

		public void handleOnStartException(Throwable ex) {
		}

		public void handleOnShutdownException(Throwable ex) {
		}
	}

}


运行结果:

线程Thread-2  1个一亿求和的结果为:  5000000050000000
线程Thread-1  2个一亿求和的结果为:  10000000100000000
线程Thread-3  3个一亿求和的结果为:  15000000150000000
线程Thread-0  4个一亿求和的结果为:  20000000200000000
线程Thread-6  5个一亿求和的结果为:  25000000250000000
线程Thread-7  6个一亿求和的结果为:  30000000300000000
线程Thread-4  7个一亿求和的结果为:  35000000350000000
线程Thread-10  8个一亿求和的结果为:  40000000400000000
线程Thread-15  9个一亿求和的结果为:  45000000450000000
线程Thread-5  10个一亿求和的结果为:  50000000500000000
线程Thread-8  11个一亿求和的结果为:  55000000550000000
...........
线程Thread-86  484个一亿求和的结果为:  2420000024200000000
线程Thread-55  485个一亿求和的结果为:  2425000024250000000
线程Thread-89  486个一亿求和的结果为:  2430000024300000000
线程Thread-41  487个一亿求和的结果为:  2435000024350000000
线程Thread-61  488个一亿求和的结果为:  2440000024400000000
线程Thread-77  489个一亿求和的结果为:  2445000024450000000
线程Thread-53  490个一亿求和的结果为:  2450000024500000000
线程Thread-77  491个一亿求和的结果为:  2455000024550000000
线程Thread-97  492个一亿求和的结果为:  2460000024600000000
线程Thread-69  493个一亿求和的结果为:  2465000024650000000
线程Thread-81  494个一亿求和的结果为:  2470000024700000000
线程Thread-93  495个一亿求和的结果为:  2475000024750000000
线程Thread-13  496个一亿求和的结果为:  2480000024800000000
线程Thread-85  497个一亿求和的结果为:  2485000024850000000
线程Thread-61  498个一亿求和的结果为:  2490000024900000000
线程Thread-69  499个一亿求和的结果为:  2495000024950000000
线程Thread-81  500个一亿求和的结果为:  2500000025000000000
500个一亿(500*(1亿求和))总共花费的时间: 13410 秒,值为:   2500000025000000000


cpu的利用率:

spacer.gifhttps://img1.sycdn.imooc.com//5b78497a0001628f04790043.jpg

三 从上面的例子,看看cpu的利用率,利用率非常高,从这里也可以看到disruptor的并行处理能力是非常强的。


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
7796
获赞与收藏
665

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消