在《disruptor(一) 单一生产者和WorkPool消费者源码阅读》介绍了单一生产者
当多个生产者向RingBuffer中写入数据时,创建Disruptor时要修改对应的参数:
Disruptor disruptor = new Disruptor(eventFactory, BUFFER_SIZE, executor, ProducerType.MULTI,
new YieldingWaitStrategy ());
ProducerType.MULTI 使用这个参数,单一生产者时,Sequencer接口的实现是SingleProducerSequencer;当是多个线程写入时,使用另一个实现MultiProducerSequencer
在写入获取RingBuffer的序列号的实现:
/**
* @see Sequencer#next(int)
*/
@Override
public long next(int n)
{
if (n < 1)
{
throw new IllegalArgumentException("n must be > 0");
}
//生产者当前写入到的序列号
long current;
//下一个序列号
long next;
//while循环主要服务CAS算法,不成功就重来
do
{
//获取生产者最新的序列号
current = cursor.get();
//要获取的序列号
next = current + n;
//wrapPoint是一个很关键的变量,这个变量决定生产者是否可以覆盖序列号nextSequence,wrapPoint是为什么是nextSequence - bufferSize;RingBuffer表现出来的是一个环形的数据结构,实际上是一个长度为bufferSize的数组,
//nextSequence - bufferSize如果nextSequence小于bufferSize wrapPoint是负数,表示可以一直生产;如果nextSequence大于bufferSize wrapPoint是一个大于0的数,由于生产者和消费者的序列号差距不能超过bufferSize
//(超过bufferSize会覆盖消费者未消费的数据),wrapPoint要小于等于多个消费者线程中消费的最小的序列号,即cachedGatingSequence的值,这就是下面if判断的根据
long wrapPoint = next - bufferSize;
//cachedGatingSequence, gatingSequenceCache这两个变量记录着上一次获取消费者中最小的消费序列号
long cachedGatingSequence = gatingSequenceCache.get();
//生产者不能继续写入,否则会覆盖消费者未消费的数据
if (wrapPoint > cachedGatingSequence || cachedGatingSequence > current)
{
//获取最新的消费者最小的消费序号
long gatingSequence = Util.getMinimumSequence(gatingSequences, current);
//依然不能满足写入条件(写入会覆盖为消费的数据)
if (wrapPoint > gatingSequence)
{
//锁一会,结束本次循环,重来
LockSupport.parkNanos(1); // TODO, should we spin based on the wait strategy?
continue;
}
//缓存一下消费者中最小的消费序列号
gatingSequenceCache.set(gatingSequence);
}
else if (cursor.compareAndSet(current, next)) //满足消费条件,有空余的空间让生产者写入,使用CAS算法,成功则跳出本次循环,不成功则重来
{
break;
}
}
while (true);
return next;
}
MultiProducerSequencer和SingleProducerSequencer next方法的区别就在于多了一个CAS算法,获取消费者最小序列号的while循环和放到外面和CAS的while循环合并