并发编程Disruptor入门

获得Disruptor
可以通过Maven或者下载jar来安装Disruptor。只要把对应的jar放在Java classpath就可以了。

基本的事件生产和消费
我们从一个简单的例子开始学习Disruptor:生产者传递一个long类型的值给消费者,而消费者消费这个数据的方式仅仅是把它打印出来。首先声明一个Event来包含需要传递的数据

public class LongEvent { 
    private long value;
    public long getValue() { 
        return value; 
    } 

    public void setValue(long value) { 
        this.value = value; 
    } 
}

由于需要让Disruptor为我们创建事件,我们同时还声明了一个EventFactory来实例化Event对象

public class LongEventFactory implements EventFactory{

    public Object newInstance() {
        return new LongEvent();
    }

}

我们还需要一个事件消费者,也就是一个事件处理器。这个事件处理器简单地把事件中存储的数据打印到终端

public class LongEventHandler implements EventHandler<LongEvent>{

    public void onEvent(LongEvent longEvent, long l, boolean b)
            throws Exception {
        System.err.println("收到数据了:"+longEvent.getValue());

    }

}

事件都会有一个生成事件的源,这个例子中假设事件是由于磁盘IO或者network读取数据的时候触发的,事件源使用一个ByteBuffer来模拟它接受到的数据,也就是说,事件源会在IO读取到一部分数据的时候触发事件(触发事件不是自动的,程序员需要在读取到数据的时候自己触发事件并发布)

public class LongEventProducer {

    private final RingBuffer<LongEvent> ringBuffer;

    public LongEventProducer(RingBuffer<LongEvent> ringBuffer) {
        this.ringBuffer = ringBuffer;
    }

    /**
     * onData用来发布事件,每调用一次就发布一次事件事件 它的参数会通过事件传递给消费者
     * 
     * @param b
     */
    public void onData(ByteBuffer b) {
        // 可以把ringBuffer看做一个事件队列,那么next就是得到下面一个事件槽
        long sequence = ringBuffer.next();
        try {
            // 用上面的索引取出一个空的事件用于填充
            LongEvent event = ringBuffer.get(sequence);
            // for the sequence
            event.setValue(b.getLong(0));
        } finally {
            // 发布事件
            ringBuffer.publish(sequence);
        }
    }
}

很明显的是:当用一个简单队列来发布事件的时候会牵涉更多的细节,这是因为事件对象还需要预先创建。发布事件最少需要两步:获取下一个事件槽并发布事件(发布事件的时候要使用try/finnally保证事件一定会被发布)。如果我们使用RingBuffer.next()获取一个事件槽,那么一定要发布对应的事件。如果不能发布事件,那么就会引起Disruptor状态的混乱。尤其是在多个事件生产者的情况下会导致事件消费者失速,从而不得不重启应用才能会恢复。

Disruptor 3.0提供了lambda式的API。这样可以把一些复杂的操作放在Ring Buffer,所以在Disruptor3.0以后的版本最好使用Event Publisher或者Event Translator来发布事件

public class LongEventProducerWithTranslator {

    // 一个translator可以看做一个事件初始化器,publicEvent方法会调用它填充Event
    private static final EventTranslatorOneArg<LongEvent, ByteBuffer> TRANSLATOR = new EventTranslatorOneArg<LongEvent, ByteBuffer>() {
        public void translateTo(LongEvent event, long sequence, ByteBuffer b) {
            event.setValue(b.getLong(0));
        }
    };
    private final RingBuffer<LongEvent> ringBuffer;

    public LongEventProducerWithTranslator(RingBuffer<LongEvent> ringBuffer) {
        this.ringBuffer = ringBuffer;
    }

    public void onData(ByteBuffer b) {
        ringBuffer.publishEvent(TRANSLATOR, b);
    }

}

上面写法的另一个好处是,Translator可以分离出来并且更加容易单元测试。Disruptor提供了不同的接口(EventTranslator, EventTranslatorOneArg, EventTranslatorTwoArg, 等等)去产生一个Translator对象。很明显,Translator中方法的参数是通过RingBuffer来传递的。

最后一步就是把所有的代码组合起来完成一个完整的事件处理系统。Disruptor在这方面做了简化,使用了DSL风格的代码(其实就是按照直观的写法,不太能算得上真正的DSL)。虽然DSL的写法比较简单,但是并没有提供所有的选项。如果依靠DSL已经可以处理大部分情况了。

public class LongEventMain {

    public static void main(String[] args) throws InterruptedException {
        // Executor that will be used to construct new threads for consumers
        Executor executor = Executors.newCachedThreadPool();
        // The factory for the event
        LongEventFactory factory = new LongEventFactory();
        // Specify the size of the ring buffer, must be power of 2.
        int bufferSize = 1024;
        // Construct the Disruptor
        Disruptor<LongEvent> disruptor = new Disruptor<LongEvent>(factory,
                bufferSize, executor);
        // Connect the handler
        disruptor.handleEventsWith(new LongEventHandler());
        // Start the Disruptor, starts all threads running
        disruptor.start();
        // Get the ring buffer from the Disruptor to be used for publishing.
        RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();

        LongEventProducer producer = new LongEventProducer(ringBuffer);

        ByteBuffer b = ByteBuffer.allocate(8);
        for (long l = 0; l<100; l++) {
            b.putLong(0, l);
            producer.onData(b);
            //Thread.sleep(1000);
        }
        disruptor.shutdown();
    }
}

得到结果:

收到数据了:0
收到数据了:1
收到数据了:2
收到数据了:3
收到数据了:4
收到数据了:5
收到数据了:6
收到数据了:7
收到数据了:8
收到数据了:9
收到数据了:10
收到数据了:11
收到数据了:12
收到数据了:13
收到数据了:14
收到数据了:15
收到数据了:16
收到数据了:17
收到数据了:18
收到数据了:19
收到数据了:20
收到数据了:21
收到数据了:22
收到数据了:23
收到数据了:24
收到数据了:25
收到数据了:26
收到数据了:27
收到数据了:28
收到数据了:29
收到数据了:30
收到数据了:31
收到数据了:32
收到数据了:33
收到数据了:34
收到数据了:35
收到数据了:36
收到数据了:37
收到数据了:38
收到数据了:39
收到数据了:40
收到数据了:41
收到数据了:42
收到数据了:43
收到数据了:44
收到数据了:45
收到数据了:46
收到数据了:47
收到数据了:48
收到数据了:49
收到数据了:50
收到数据了:51
收到数据了:52
收到数据了:53
收到数据了:54
收到数据了:55
收到数据了:56
收到数据了:57
收到数据了:58
收到数据了:59
收到数据了:60
收到数据了:61
收到数据了:62
收到数据了:63
收到数据了:64
收到数据了:65
收到数据了:66
收到数据了:67
收到数据了:68
收到数据了:69
收到数据了:70
收到数据了:71
收到数据了:72
收到数据了:73
收到数据了:74
收到数据了:75
收到数据了:76
收到数据了:77
收到数据了:78
收到数据了:79
收到数据了:80
收到数据了:81
收到数据了:82
收到数据了:83
收到数据了:84
收到数据了:85
收到数据了:86
收到数据了:87
收到数据了:88
收到数据了:89
收到数据了:90
收到数据了:91
收到数据了:92
收到数据了:93
收到数据了:94
收到数据了:95
收到数据了:96
收到数据了:97
收到数据了:98
收到数据了:99
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值