Disruptor源码分析(五)WaitStrategy

针对不同的应用场景,为了尽量提高处理性能,Disruptor提供了多种生产者和消费者之间的同步策略。

在此只对常用的四种同步策略的实现进行说明,其它实现不在详述。

  • BlockingWaitStrategy

BlockingWaitStrategy源码如下:

public final class BlockingWaitStrategy implements WaitStrategy
{
    private final Object mutex = new Object();

    @Override
    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;
    }

    @Override
    public void signalAllWhenBlocking()
    {
        synchronized (mutex)
        {
            mutex.notifyAll();
        }
    }

    @Override
    public String toString()
    {
        return "BlockingWaitStrategy{" +
            "mutex=" + mutex +
            '}';
    }
}

从上述代码可以看出,BlockingWaitStrategy类通过Java内置的synchronized关键字和Object类的wait方法和notifyAll方法,实现生产者和消费者之间的同步。虽然Java1.8版本对synchronized关键字进行了优化,根据并发情况,实现了从无锁状态、偏向锁、轻量级锁、重量级锁的转换,尽量不使用操作系统层次的mutex锁。

 

跟其它几种等待策略相比,其效率还是比较低的。

它适用的场景:对系统的吞吐量和低延时要求不高,但对CPU资源要求更高。

  • SleepingWaitStrategy

SleepingWaitStrategy源码如下:

public final class SleepingWaitStrategy implements WaitStrategy
{
    private static final int DEFAULT_RETRIES = 200;
    private static final long DEFAULT_SLEEP = 100;

    private final int retries;
    private final long sleepTimeNs;

    public SleepingWaitStrategy()
    {
        this(DEFAULT_RETRIES, DEFAULT_SLEEP);
    }

    public SleepingWaitStrategy(int retries)
    {
        this(retries, DEFAULT_SLEEP);
    }

    public SleepingWaitStrategy(int retries, long sleepTimeNs)
    {
        this.retries = retries;
        this.sleepTimeNs = sleepTimeNs;
    }

    @Override
    public long waitFor(
        final long sequence, Sequence cursor, final Sequence dependentSequence, final SequenceBarrier barrier)
        throws AlertException
    {
        long availableSequence;
        int counter = retries;

        while ((availableSequence = dependentSequence.get()) < sequence)
        {
            counter = applyWaitMethod(barrier, counter);
        }

        return availableSequence;
    }

    @Override
    public void signalAllWhenBlocking()
    {
    }

    private int applyWaitMethod(final SequenceBarrier barrier, int counter)
        throws AlertException
    {
        barrier.checkAlert();

        if (counter > 100)
        {
            --counter;
        }
        else if (counter > 0)
        {
            --counter;
            Thread.yield();
        }
        else
        {
            LockSupport.parkNanos(sleepTimeNs);
        }

        return counter;
    }
}
 

从上述代码可以看出,SleepingWaitStrategy类通过Thread.yield 和LockSupport.parkNanos方法,实现生产者和消费者之间的同步。

waitFor方法的处理逻辑如下:

如果没有可用的序列号,则:

首先,自旋重试100次(此值可设置,默认200次),如果在重试过程中,存在可用的序列号,则直接返回可用的序列号。

否则,如果重试指定次数以后,还是没有可用序列号,则继续自旋重试,但这时每重试一次,便调用Thread.yield方法,放弃CPU的使用权,让其它线程可以使用CPU。当该线程再次获取CPU使用权时,继续重试,如果还没有可用的序列号,则继续放弃CPU使用权等待。此循环最多100次。

最后,如果还没有可用的序列号,则调用LockSupport.parkNanos方法阻塞线程,直到存在可用的序列号。当LockSupport.parkNanos方法由于超时返回后,如果还没有可用的序列号,则该线程获取CPU使用权以后,可能继续调用LockSupport.parkNanos方法阻塞线程。

跟其它几种等待策略相比,这是一种在性能和CPU资源之间折中的方案。

  • YieldingWaitStrategy

YieldingWaitStrategy源码如下:

public final class YieldingWaitStrategy implements WaitStrategy
{
    private static final int SPIN_TRIES = 100;

    @Override
    public long waitFor(
        final long sequence, Sequence cursor, final Sequence dependentSequence, final SequenceBarrier barrier)
        throws AlertException, InterruptedException
    {
        long availableSequence;
        int counter = SPIN_TRIES;

        while ((availableSequence = dependentSequence.get()) < sequence)
        {
            counter = applyWaitMethod(barrier, counter);
        }

        return availableSequence;
    }

    @Override
    public void signalAllWhenBlocking()
    {
    }

    private int applyWaitMethod(final SequenceBarrier barrier, int counter)
        throws AlertException
    {
        barrier.checkAlert();

        if (0 == counter)
        {
            Thread.yield();
        }
        else
        {
            --counter;
        }

        return counter;
    }
}

从上述代码可以看出,YieldingWaitStrategy类通过Thread.yield方法,实现生产者和消费者之间的同步。

waitFor方法的处理逻辑如下:如果没有可用的序列号,则:

首先,自旋重试100次(此值可设置,默认100次),如果在重试过程中,存在可用的序列号,则直接返回可用的序列号。

否则,如果重试指定次数以后,还是没有可用序列号,则调用Thread.yield方法,放弃CPU的使用权,让其它线程可以使用CPU。当该线程再次获取CPU使用权时,继续重试,如果还没有可用的序列号,则继续放弃CPU使用权等待。

跟其它几种等待策略相比,该策略100%使用CPU。

  • BusySpinWaitStrategy

BusySpinWaitStrategy源码如下:

public final class BusySpinWaitStrategy implements WaitStrategy
{
    @Override
    public long waitFor(
        final long sequence, Sequence cursor, final Sequence dependentSequence, final SequenceBarrier barrier)
        throws AlertException, InterruptedException
    {
        long availableSequence;

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

        return availableSequence;
    }

    @Override
    public void signalAllWhenBlocking()
    {
    }
}

从上述代码可以看出,BusySpinWaitStrategy类通过ThreadHints.onSpinWait()方法,实现生产者和消费者之间的同步。

waitFor方法的处理逻辑如下:如果没有可用的序列号,则:

调用ThreadHints.onSpinWait方法。在ThreadHints.onSpinWait方法中,如果JDK的版本支持java.lang.Thread.onSpinWait()方法,则调用。否则,直接返回。

该策略与YieldingWaitStrategy策略相比,很可能出现当没有可用序列号时,长期占用CPU,不释放CPU使用权,导致其它线程无法获取CPU使用权。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

任性之闲来无事

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值