一、基本概念
环形无锁高速队列。
这里的无锁是指:不使用锁来保证原子性,而通过使用CAS保证原子性。
数组下标和Sequence的关系:
假设我创建了一个长度大小为8的环形数组,这意味着该数组下标只能是从0-7。而序列号Sequence可以认为是当前生产者已经生产到了第几个产品了。Sequence %数组长度 = Sequence对应的数组下标。
每生产一个,序列号就会自增,序列号的大小是2的64次幂-1,因为它是long类型的。
如何保证生产者不会把为消费的数据给覆盖了?(假设ringBufferSize=8)
要知道我们的数组是可以复用的,因为我们的数组是环形的(逻辑上),例如 生产者从下标为0生产到下标为7的位置,而消费者也将下标为0-4的数据给消费掉了,那么生产者就又可以重头开始继续生产,将新生产的数据从下标0放到4,覆盖掉旧数据。即边消费边生产的过程。
回归正传,如何防止生产者把未消费的数据给覆盖了?假如当前消费者消费到了sequence为10的位置(此时sequence10的位置也已经消费掉了,而sequence11的位置还没有被消费),而生产者即将生产的sequence值为18,此时就会进行18-10(生产者的sequence-ringBufferSize)=10,判断是10 > 10,发现不成立,那么生产者可以生产(因为sequence10的位置已经被消费了)。然后生产者会继续生产,此时即将生产的sequence为19,此时19-8=11 > 10,此时生产者就不能生产了(因为11的这个位置还没有被消费)。
上面是单消费者的情况,对于多消费者也是类似。如果是多消费者,那么上面的消费者seqence就是指众多消费者中senquence最小的那个。
上面为什么要 (生产者的Seqence - RingBufferSize)
因为生产者和消费者的seqence之差不会超过一圈,也就是不会超过ringBufferSize,毕竟这是一个环形数组。相差最大的时候就是 消费者没有消费,而生产者生产了一圈。如果相差超过ringBufferSize,那么这个时候就会出现覆盖未消费的数据。
下面是获取下一个sequence号的源码
public long next() {
return this.next(1);
}
public long next(int n) {
if (n < 1) {
throw new IllegalArgumentException("n must be > 0");
} else {
long nextValue = this.nextValue;
long nextSequence = nextValue + (long)n;
long wrapPoint = nextSequence - (long)this.bufferSize;
long cachedGatingSequence = this.cachedValue;
if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue) {
this.cursor.setVolatile(nextValue);
long minSequence;
while(wrapPoint > (minSequence = Util.getMinimumSequence(this.gatingSequences, nextValue))) {
LockSupport.parkNanos(1L);
}
this.cachedValue = minSequence;
}
this.nextValue = nextSequence;
return nextSequence;
}
}
Disruptor的优点:
1、无锁,利用CAS进行处理,而无需加锁,提高了速度。(相对比jdk自带的xxxBlockedQueue快)
2、可以复用数组,提高JVM的内存利用率,减少GC次数,同时可以防止应生产者过快生产,而导致内存不足的风险。(djk也有利用CAS的无锁队列,但是存在生产者生产过快而导致内存不足的风险)
二、单生产者单消费者DEMO
<!-- https://mvnrepository.com/artifact/com.lmax/disruptor -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
1、生产者
package com.jxf.disruptor.single;
import com.lmax.disruptor.RingBuffer;
public class StringEventProducer {
private RingBuffer<StringEvent> ringBuffer;
public StringEventProducer(
RingBuffer<StringEvent> ringBuffer) {
this.ringBuffer = ringBuffer;
}
/**
* 生产数据
*/
public void setData(String data) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 1、获取待生产下一个的序号
long sequeuence = ringBuffer.next();
try {
// 2、获取下一个待生产的 数据(空)
StringEvent stringEvent = ringBuffer.get(sequeuence);
// 3、填充产品数据
stringEvent.setValue(data);
System.out.println("生产-->" + data);
} finally {
// 4、发布产品。消费者可以去消费。
ringBuffer.publish(sequeuence);
}
}
}
2、生产者生产的数据对象(在队列中传输的对象)
package com.jxf.disruptor.single;
import lombok.Data;
@Data
/**
* 放在队列中的对象(数据)
*/
public class StringEvent {
private String value;
}
3、消费者
package com.jxf.disruptor.single;
import com.lmax.disruptor.EventHandler;
/**
* 消费者
*/
public class StringEventHandler implements EventHandler<StringEvent> {
/**
* 消费数据的方法
*
* @param stringEvent 被消费的数据
* @param sqeueuence 消费序号 从0 到 2的64次幂-1(long类型的大小)
* @param endOfBatch 当前是否已经消费到尽头了(此刻没有下一个需要消费的数据)
*/
@Override
public void onEvent(StringEvent stringEvent, long sqeueuence, boolean endOfBatch) {
System.out.println(
"消费 -->" + stringEvent.getValue() + " sqeueuence-->" + +sqeueuence + " endOfBatch-->"
+ endOfBatch);
}
}
4、测试
package com.jxf.disruptor.single;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class MainTest {
public static void main(String[] args) {
// 创建一个线程工厂提供线程来触发Consumer的事件处理
ThreadFactory threadFactory = Executors.defaultThreadFactory();
// 环形数组大小
int ringBufferSize = 16;
// 第一个参数是事件工厂。用于生产事件(数据)
Disruptor<StringEvent> disruptor = new Disruptor<>(() -> new StringEvent(), ringBufferSize,
threadFactory, ProducerType.SINGLE, new YieldingWaitStrategy());
// 连接消费者方法
disruptor.handleEventsWith(new StringEventHandler());
// 启动消费者
disruptor.start();
RingBuffer ringBuffer = disruptor.getRingBuffer();
// 创建生产者
StringEventProducer eventProducer = new StringEventProducer(ringBuffer);
for (int i = 1; i <= 1000; i++) {
// 生产数据
eventProducer.setData("数据" + i);
}
disruptor.shutdown();
}
}
三、多生产者多消费者(竞争关系-非重复消费)DEMO
即队列中的一个数据只会被其中的一个消费者去消费。worker模式,即竞争模式
1、队列传输对象
package com.jxf.disruptor.multi;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class Order {
private String id;
}
2、生产者
package com.jxf.disruptor.multi;
import com.lmax.disruptor.RingBuffer;
public class OrderProducer {
private RingBuffer<Order> ringBuffer;
public OrderProducer(
RingBuffer<Order> ringBuffer) {
this.ringBuffer = ringBuffer;
}
/**
* 生产数据
*/
public void setData(String data) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 1、获取待生产下一个的序号
long sequeuence = ringBuffer.next();
try {
// 2、获取下一个待生产的 数据(空)
Order order = ringBuffer.get(sequeuence);
// 3、填充产品数据
order.setId(data);
System.out.println("生产者 生产-->" + order);
} finally {
// 4、发布产品。消费者可以去消费。
ringBuffer.publish(sequeuence);
}
}
}
3、消费者
package com.jxf.disruptor.multi;
import com.lmax.disruptor.WorkHandler;
/**
* 消费者
*/
public class OrderWorkHandler implements WorkHandler<Order> {
// 消费者编号
private int id;
public OrderWorkHandler(int id) {
this.id = id;
}
@Override
public void onEvent(Order order) throws Exception {
System.out.println("消费者" + id + " 消费-->" + order);
}
}
4、队列的异常处理
package com.jxf.disruptor.multi;
import com.lmax.disruptor.ExceptionHandler;
/**
* 队列异常时的处理
*/
public class OrderExceptionHandler implements ExceptionHandler<Order> {
@Override
public void handleEventException(Throwable throwable, long l, Order order) {
}
@Override
public void handleOnStartException(Throwable throwable) {
}
@Override
public void handleOnShutdownException(Throwable throwable) {
}
}
5、整合测试
package com.jxf.disruptor.multi;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.SequenceBarrier;
import com.lmax.disruptor.WorkerPool;
import com.lmax.disruptor.YieldingWaitStrategy;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainTest {
public static void main(String[] args) {
// 环形数组大小
int ringBufferSize = 1024 * 1024;
// 创建一个线程工厂提供线程来触发Consumer的事件处理
RingBuffer<Order> ringBuffer = RingBuffer
.createMultiProducer(() -> new Order(), ringBufferSize, new YieldingWaitStrategy());
// 通过ringBuffer创建一个屏障
SequenceBarrier barrier = ringBuffer.newBarrier();
// 创建多个消费者
OrderWorkHandler[] workHandlers = new OrderWorkHandler[16];
for (int i = 0; i < workHandlers.length; i++) {
workHandlers[i] = new OrderWorkHandler(i);
}
// 创建工作池
WorkerPool<Order> workerPool = new WorkerPool<>(ringBuffer, barrier,
new OrderExceptionHandler(), workHandlers);
// 创建消费者线程池(创建16个线程的线程池,每个消费者可以对应一个线程)
ExecutorService threadPool = Executors.newFixedThreadPool(workHandlers.length);
// 启动工作池(此时我们的队列就在允许中了...消费者也在等待数据消费)
workerPool.start(threadPool);
// 模拟100个生产者,每个生产者生产100条消息
for (int i = 1; i <= 100; i++) {
OrderProducer orderProducer = new OrderProducer(ringBuffer);
for (int j = 1; j <= 100; j++) {
orderProducer.setData(UUID.randomUUID().toString());
}
}
}
}
四、多生产者多消费者(独立关系-重复消费)DEMO
说明:和2的区别就是,2的每个消费者不会重复消费队列中的数据。而这里的3的消费者是独立关系,会重复消费队列中的数据。(应该结合应用场景选择2或者3)
代码和2差不多
变动一:消费者实现EventHandler接口。(之前的wokerHandler是woker模式,是竞争模式)
package com.jxf.disruptor.multi;
import com.lmax.disruptor.EventHandler;
/**
* 消费者
*/
public class OrderEventHandler implements EventHandler<Order> {
// 消费者编号
private int id;
public OrderEventHandler(int id) {
this.id = id;
}
@Override
public void onEvent(Order order, long sqeueuence, boolean endOfBatch) throws Exception {
System.out.println(
"消费者" + id + "消费 -->" + order.getId() + " sqeueuence-->" + +sqeueuence
+ " endOfBatch-->"
+ endOfBatch);
}
}
变动二:Main方法
package com.jxf.disruptor.multi;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.SequenceBarrier;
import com.lmax.disruptor.WorkerPool;
import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
public class MainTest2 {
public static void main(String[] args) {
// 环形数组大小
int ringBufferSize = 1024 * 1024;
// 创建多个消费者
OrderEventHandler[] eventHandlers = new OrderEventHandler[16];
for (int i = 0; i < eventHandlers.length; i++) {
eventHandlers[i] = new OrderEventHandler(i);
}
Disruptor<Order> disruptor = new Disruptor<>(() -> new Order(), ringBufferSize,
Executors.defaultThreadFactory(), ProducerType.MULTI, new YieldingWaitStrategy());
disruptor.setDefaultExceptionHandler(new OrderExceptionHandler());
// 异常处理
disruptor.handleEventsWith(eventHandlers);
// 启动队列(启动消费者)
disruptor.start();
RingBuffer<Order> ringBuffer = disruptor.getRingBuffer();
// 模拟100个生产者,每个生产者生产100条消息
for (int i = 1; i <= 5; i++) {
OrderProducer orderProducer = new OrderProducer(ringBuffer);
for (int j = 1; j <= 1; j++) {
orderProducer.setData(UUID.randomUUID().toString());
}
}
}
}