针对不同的应用场景,为了尽量提高处理性能,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使用权。