LMAX Disruptor作为阻塞队列?

时间:2016-06-29 09:08:40

标签: java multithreading performance garbage-collection disruptor-pattern

我有什么方法可以在一个结构中同时拥有 -

  1. BlockingQueue的语义,即 - 非阻塞查看,阻止轮询和阻塞put。多个提供者一个消费者。
  2. RingBuffer,它有效地作为对象池工作,因此我不想将新对象放在环形缓冲区中,而是希望在那里重用现有对象,复制状态。所以基本上LMAX disruptor的功能开箱即用。
  3. 是否有类似的东西? 我想我可以尝试使用Disruptor,我已经可以使用它作为阻塞队列阻塞(如果环形缓冲区是#34;完全")如果我理解正确的话。它已经有了#34;可重复使用的对象"我需要的语义。所以唯一的问题是如何创建一个能够PULL对象(而不是使用回调)的客户端,因为我并不熟悉内部的Disruptor结构 - 可以这样做吗?使用所有这些序列发生器,创建一个新的EventProcessor或类似的东西?

    不是,在客户端拥有阻塞队列并从中获取阻塞队列的明显解决方案并不是一个理想的解决方案,因为它打破了使用破坏程序对象池的全部要点 - 您需要拥有现在可以使用新池,或者只是在放入阻塞队列之前在回调中创建一个新对象,而且我根本不想创建任何垃圾。

    有没有办法用Disruptor或任何其他面向性能/无垃圾的Java库来实现它?

2 个答案:

答案 0 :(得分:4)

我们今年早些时候开放了包含DiruptorBlockingQueue的Conversant Diruptor。您可以在github

上找到代码

Conversant Disruptor几乎可以包含在任何项目中,因为它支持BlockingQueue api并在Maven Central上发布。

答案 1 :(得分:2)

对于好奇,我无法从Disruptor本身获得“阻塞拉”语义,但当然,为非阻塞拉动添加“阻塞”功能是微不足道的。 “窥视”功能本身是可能的但效率不高(你需要在每次偷看时反复复制项目),并且可以通过缓存“民意调查”的结果来替换。

所以,最小的原始解决方案,只实现了我需要的方法:

public class DisruptorMPSCQueue<T extends ICopyable<T>> {

    private final RingBuffer<T> ringBuffer;
    private final EventPoller<T> eventPoller;
    private T tempPolledEvent;

    private EventPoller.Handler<T> pollerHandler = new EventPoller.Handler<T>() {
        @Override
        public boolean onEvent(final T event, final long sequence, final boolean endOfBatch) throws Exception {
            tempPolledEvent.copyFrom(event);
            return false;
        }
    };

    public DisruptorMPSCQueue(EventFactory<T> typeConstructor, int size) {
        ringBuffer = RingBuffer.createMultiProducer(typeConstructor, size);
        eventPoller = ringBuffer.newPoller();
        ringBuffer.addGatingSequences(eventPoller.getSequence());
    }

    /**
     * Blocking, can be called from any thread, the event will be copied to the ringBuffer
     */
    public void put(final T event) {
        long sequence = ringBuffer.next(); // blocked by ringBuffer's gatingSequence
        ringBuffer.get(sequence).copyFrom(event);
        ringBuffer.publish(sequence);
    }

    /**
     * Not blocking, can be called from any thread, the event will be copied to the ringBuffer
     *
     * @throws IllegalStateException if the element cannot be added at this time due to capacity restrictions
     */
    public void offer(final T event) {
        long sequence;
        try {
            sequence = ringBuffer.tryNext();
        } catch (InsufficientCapacityException e) {
            throw new IllegalStateException(e); // to mimic blockingQueue
        }
        ringBuffer.get(sequence).copyFrom(event);
        ringBuffer.publish(sequence);
    }

    /**
     * Retrieve top of the queue(removes from the queue). NOT thread-safe, can be called from one thread only.
     *
     * @param destination top of the queue will be copied to destination
     * @return destination object or null if the queue is empty
     */
    public T poll(final T destination) {
        try {
            tempPolledEvent = destination;  // yea, the poller usage is a bit dumb
            EventPoller.PollState poll = eventPoller.poll(pollerHandler);
            if (poll == EventPoller.PollState.PROCESSING) {
                return tempPolledEvent;
            } else {
                return null;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}