Disruptor源码分析之消费端

启动Disruptor,原子类型判断启动状态,只能启动一次,ConsumerRepository实现了Iterable接口,则遍历的是consumerInfos集合

public RingBuffer<T> start()
{
    checkOnlyStartedOnce();
    for (final ConsumerInfo consumerInfo : consumerRepository)
    {
        consumerInfo.start(executor);
    }

    return ringBuffer;
}

private void checkOnlyStartedOnce()
{
    if (!started.compareAndSet(false, true))
    {
        throw new IllegalStateException("Disruptor.start() must only be called once.");
    }
}

启动整个工作线程池,只有一个工作池


public void start(Executor executor)
{
    workerPool.start(executor);
}

 

里面包含多个工作任务,原子类型判断设置状态,获取当前提供端的下标并设置到消费池的消费定序器下标中workSequence以及各个消费端的定序器

public RingBuffer<T> start(final Executor executor)
{
    if (!started.compareAndSet(false, true))
    {
        throw new IllegalStateException("WorkerPool has already been started and cannot be restarted until halted.");
    }

    final long cursor = ringBuffer.getCursor();
    workSequence.set(cursor);

    for (WorkProcessor<?> processor : workProcessors)
    {
        processor.getSequence().set(cursor);
        executor.execute(processor);
    }

    return ringBuffer;
}

在BasicExecutor中创建消费线程并启动

public void execute(Runnable command)
{
    final Thread thread = factory.newThread(command);
    if (null == thread)
    {
        throw new RuntimeException("Failed to create thread to run: " + command);
    }

    thread.start();

    threads.add(thread);
}

 

执行消费线程,判断设置运行状态,清除中断标志,主体是个死循环,除非有中断异常产生并且线程不在运行标志,发布开始事件,先获取本线程要处理的定序器下标,因为各个消费线程都是共用的一个workSequence,所以通过原子类型设置方法来获取各自消费线程所需要从头开始消费的下标,达到了互斥消费,默认下标都是从-1开始。

public void run()
{
    if (!running.compareAndSet(false, true))
    {
        throw new IllegalStateException("Thread is already running");
    }
    sequenceBarrier.clearAlert();

    notifyStart();

    boolean processedSequence = true;
    long cachedAvailableSequence = Long.MIN_VALUE;
    long nextSequence = sequence.get();
    T event = null;
    while (true)
    {
        try
        {
            // if previous sequence was processed - fetch the next sequence and set
            // that we have successfully processed the previous sequence
            // typically, this will be true
            // this prevents the sequence getting too far forward if an exception
            // is thrown from the WorkHandler
            if (processedSequence)
            {
                processedSequence = false;
                do
                {
                    nextSequence = workSequence.get() + 1L;
                    sequence.set(nextSequence - 1L);
                }
                while (!workSequence.compareAndSet(nextSequence - 1L, nextSequence));
            }

            if (cachedAvailableSequence >= nextSequence)
            {
                event = ringBuffer.get(nextSequence);
                workHandler.onEvent(event);
                processedSequence = true;
            }
            else
            {
                cachedAvailableSequence = sequenceBarrier.waitFor(nextSequence);
            }
        }
        catch (final TimeoutException e)
        {
            notifyTimeout(sequence.get());
        }
        catch (final AlertException ex)
        {
            if (!running.get())
            {
                break;
            }
        }
        catch (final Throwable ex)
        {
            // handle, mark as processed, unless the exception handler threw an exception
            exceptionHandler.handleEventException(ex, nextSequence, event);
            processedSequence = true;
        }
    }

    notifyShutdown();

    running.set(false);
}

一次获取一个消费下标,但是查询的时候获取当前可消费的最大下标,可以减少获取频率。通过统一定序进程来获取可以消费的下标,检查中断标志是否正常。

public long waitFor(final long sequence)
        throws AlertException, InterruptedException, TimeoutException
{
    checkAlert();

    long availableSequence = waitStrategy.waitFor(sequence, cursorSequence, dependentSequence, this);

    if (availableSequence < sequence)
    {
        return availableSequence;
    }

    return sequencer.getHighestPublishedSequence(sequence, availableSequence);
}

根据不同的等待策略执行不同的获取消息逻辑,cursorSequence为提供端的下标,当没有消息产生时线程会等待mutex.wait(),被唤醒后也会检查依赖的其他线程的定序器dependentSequence是否已经消费过,这里的依赖依然是提供者的定序器,否的话就暂时自旋等待并且循环检查条件是否满足。

public long waitFor(long sequence, Sequence cursorSequence, Sequence dependentSequence, SequenceBarrier barrier)
        throws AlertException, InterruptedException
{
    long availableSequence;
    if (cursorSequence.get() < sequence)
    {
        synchronized (mutex)
        {
            while (cursorSequence.get() < sequence)
            {
                barrier.checkAlert();
                mutex.wait();
            }
        }
    }

    while ((availableSequence = dependentSequence.get()) < sequence)
    {
        barrier.checkAlert();
        ThreadHints.onSpinWait();
    }

    return availableSequence;
}

 

获取到合适的下标后判断是都小于需要的下标,否的话直接返回,当然对于默认策略是不会进入到这里的,最后再校验可用的下标的对应数据标志位是否真的可用,因为提供者是先设置移动定序器下标,再往里面放数据以及设置可用标志的。

public long getHighestPublishedSequence(long lowerBound, long availableSequence)
{
    for (long sequence = lowerBound; sequence <= availableSequence; sequence++)
    {
        if (!isAvailable(sequence))
        {
            return sequence - 1;
        }
    }

    return availableSequence;
}

保存上次获取的可用下标,判断是否大于当前需要的下标,然后获取当前下标的数组元素对象,执行对应的处理,消费线程处理workHandler.onEvent(event);最后把processedSequence设置为true,这样下一次循环就重新从workSequence中获取互斥的下标。

public E get(long sequence)
{
    return elementAt(sequence);
}

protected final E elementAt(long sequence)
{
    return (E) UNSAFE.getObject(entries, REF_ARRAY_BASE + ((sequence & indexMask) << REF_ELEMENT_SHIFT));
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值