disruptor无锁消息队列,使用ringbuffer来实现无锁消息队列
正常多线程访问访问同一个资源的时候是需要进行加锁的,
如synchronized方法。但是使用synchronized方法的时候会使cpu进行等待,
在高并发的情况下会消耗大量的cpu资源。
为了解决这个问题,使用无锁消息队列+共享指针的方式。
解决了多线程访问同一个资源的加锁问题
和多线程之间复制数据导致效率低下的问题(共享指针)
上图为ringbuffer的数据结构,底层的数据结构为数组结构,
在往ringbuffer中存数据的时候,会使用当前的序号对ringbuffer的大小进行取余数运算
然后在余数的位置index直接使用新的值去覆盖旧的值,这样就避免了垃圾回收的过程。 index小于数组的大小,
在进行消费的时候,
workSequence和sequence。
多个线程共用一个sequence,sequence指向未被消费过的元素的位置。
当生产者往队列里面插入元素的时候,就会判断如果插入元素的位置,大于等于sequence,就不会继续往里面进行插入。
多个线程共用一个workSequence,workSequence指向可以被消费的元素的最大位置。
多个线程竞争序号workSequence (CAS机制)。
ringbuffer的使用示例,
1、创建一个事件类型。
package com.bjpowernode.consumer;
public class StringEvent {
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
private String value;
}
2、创建一个事件工厂类。
package com.bjpowernode.consumer;
import com.lmax.disruptor.EventFactory;
/**
* 定义事件工厂
*/
public class StringEventFactory implements EventFactory<StringEvent> {
@Override
public StringEvent newInstance() {
return new StringEvent();
}
}
3、创建一个事件处理类。
package com.bjpowernode.consumer;
import com.lmax.disruptor.EventHandler;
public class StringEventHandler implements EventHandler<StringEvent> {
public StringEventHandler() {
}
@Override
public void onEvent(StringEvent event,long sequence,boolean endOfBatch) throws Exception{
System.out.println("consumer:"+event.getValue()+"--->sequence="+sequence+",endOfBatchc="+endOfBatch);
}
}
4、生产者往ringbuffer无锁队列中生产事件。
package com.bjpowernode.provider;
import com.bjpowernode.consumer.StringEvent;
import com.lmax.disruptor.RingBuffer;
public class StringEventProducer {
/**
* 核心类环形数组这个类
*/
public final RingBuffer<StringEvent> ringBuffer;
/**
* 通过构造方法初始化环形数组这个核心类
* @param ringBuffer
*/
public StringEventProducer(RingBuffer<StringEvent> ringBuffer) {
this.ringBuffer = ringBuffer;
}
/**
* 生产事件(数据)的一个方法
* 其实就是往环形数组里面放数据
* @param data
*/
public void onData(String data){
/**
* ringBuffer获取下一个序号*/
long sequence = ringBuffer.next();
try{
/**2、取出空的事件队列*/
StringEvent stringEvent = ringBuffer.get(sequence);
/**3、获取事件队列传递的数据*/
stringEvent.setValue(data);
}finally {
/**4、发布事件(把事件放到环形数组里面去)*/
ringBuffer.publish(sequence);
System.out.println("producer produce data"+ sequence);
}
}
}
5、ringbuffer测试事件的生产与消费。
package com.bjpowernode.sample;
import com.bjpowernode.consumer.StringEvent;
import com.bjpowernode.consumer.StringEventFactory;
import com.bjpowernode.consumer.StringEventHandler;
import com.bjpowernode.provider.StringEventProducer;
import com.lmax.disruptor.EventFactory;
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.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class Test {
public static void main(String[] args) {
ThreadFactory threadFactory = Executors.defaultThreadFactory();
EventFactory<StringEvent> eventFactory = new StringEventFactory();
int ringBufferSize = 16;
Disruptor<StringEvent> disruptor = new Disruptor<StringEvent>(eventFactory,
ringBufferSize,
threadFactory,
ProducerType.SINGLE,
new YieldingWaitStrategy()
);
disruptor.handleEventsWith(new StringEventHandler());
disruptor.start();
/**
* 消费者启动了
* -----------------------------------
* 下面开始生产
*/
/**
* 创建RingBuffer容器
*/
RingBuffer<StringEvent> ringBuffer = disruptor.getRingBuffer();
/**
* 创建生产者
*/
StringEventProducer producer = new StringEventProducer(ringBuffer);
for (int i = 0; i < 10000; i++) {
producer.onData(String.valueOf(i));
}
/**
* 关闭disruptor 和 executor
*
*/
disruptor.shutdown();
}
}