无锁并发框架Disruptor

Disruptor:

介绍:是一个高性能的异步处理框架,研发的初衷是为了解决高并发情况下队列加锁的问题,使用效果和BlockingQueue很类似,号称“一个线程一秒可以处理600万订单”(惊呆了);

核心设计组件
RingBuffer:环形缓冲区,其内部定义了数组、sequencer等属性,利用数组存储结构达到了环形数组的效果;但存储的数组存储达或获取到最后一位时,又开始重第一位开始存储获取获取;
无锁设计:每个生产者或消费者会先申请可以操作的元素在数组中的位置,申请到之后,直接在该下标位置进行读取或写入;
数组的长度只能是2的指数次幂,利用位运算,加快下标的定位;
在这里插入图片描述
sequence序号:利用生产者和消费者维护的序号来定位ringBuffer的位置,并且这个sequence是一直递增的,添加一个元素或消费一个元素,对应消费者和生产者各自维护的sequence就会加1;比如说一个RingBuffer的大小是8,序号从0开始增长,当sequence=7时,相当于转了一圈,如果继续增长的话,下一个获取或者写入的下标会从0开始;也即是说sequence%ringBuffer.length取模计算出操作ringBuffer的下标;
由于涉及到取模操作,没有位运算高效,所以为了使用位运算,定义RingBuffer的大小是2的N次方;hashMap中的h & (length-1); h=sequence
这里发现了和其它队列不同的的是没有元素的删除操作,只有覆盖操作,数组里的元素对象都是复用的;

无锁机制:
在生产者/消费者模式下,distuptor号称“无锁框架”(BlockingQueue是利用Lock锁做到的),这是怎么做到的那?

一个生产者+一个消费者:
生产者自己维护了一个sequence序号,消费者自己维护了sequence序号,两者各操作各的,不需要加锁;但是需要注意的是两者操作的下标不能大于环形数组一圈,也就是当一个下标中的元素没有被取出时,生产者不能对这个下标位置的元素进行覆盖的,需要进行等待该下标元素被消费者取出之后才能被覆盖,这里的等待策略有很多种,后面介绍;
同样消费者如果消费速度过快,则也需要等待,也是使用四种等待策略,当要读取的下标有元素时,该消费者就会唤醒等待的消费者;

N个消费者+N个生产者
每个消费者都各自维护者自己的sequence序号,多个生产者只维护了一个sequence序号,否则数据就乱套了,这里是通过CAS来保证生产者sequence序号的线程安全,也是没有用到锁;这里生产者的sequence序号也是不能超过消费者最小sequence序号一圈的,就是任意消费者的下标元素还没消费,就不能被生产者覆盖;

等待策略:都是实现了WaitStrategy接口
Disruptor的默认策略是BlockingWaitStrategy。在BlockingWaitStrategy内部是使 用锁和condition来控制线程的唤醒。BlockingWaitStrategy是最低效的策略,但其对CPU 的消耗最小并且在各种不同部署环境中能提供更加一致的性能表现。SleepingWaitStrategy
SleepingWaitStrategy 的性能表现跟 BlockingWaitStrategy 差不多,对 CPU 的消耗也类似,但其对生产者线程的影响最小,通过使用LockSupport.parkNanos(1)来实现循环等待。一般来说Linux系统会暂停一个线程约60µs,这样做的好处是,生产线程不需要采取任何其他行动就可以增加适当的计数器,也不需要花费时间信号通知条件变量。但是,在生产者线程和使用者线程之间移动事件的平均延迟会更高。它在不需要低延迟并且对生产线程的影响较小的情况最好。一个常见的用例是异步日志记录。
YieldingWaitStrategy:YieldingWaitStrategy是可以使用在低延迟系统的策略之一。YieldingWaitStrategy 将自旋以等待序列增加到适当的值。在循环体内,将调用Thread.yield(),以允许其他排队的线程运行。在要求极高性能且事件处理线数小于 CPU 逻辑核心数的场景中,推荐使用此策略;例如,CPU开启超线程的特性。
BusySpinWaitStrategy:性能最好,适合用于低延迟的系统。在要求极高性能且事件处理线程数小于CPU逻辑核心数的场景中,推荐使用此策略;例如,CPU开启超线程的特性。

下面直接演示disrutor的用法:

/**
 * 定义event ; 业务数据对象,生成和消费的业务数据
 */
@Data
public class OrderEvent {
    private int id;
}


/**
 * 定义业务数据(事件)工厂
 */
public class OrderEventFactory implements EventFactory<OrderEvent> {
    @Override
    public OrderEvent newInstance() {
        return new OrderEvent();
    }
}


/**
 * 定义消费者(事件处理器)
 */
public class OrderEventHandler implements EventHandler<OrderEvent> {
    @Override
    public void onEvent(OrderEvent orderEvent, long l, boolean b) throws Exception {
        System.out.println(Thread.currentThread().getName()+"消费者:"+orderEvent.getId());
    }
}



/**
 * 生产者
 */
public class OrderEventProducer {
    private final RingBuffer<OrderEvent> ringBuffer;

    public OrderEventProducer(RingBuffer<OrderEvent> ringBuffer) {
        this.ringBuffer = ringBuffer;
    }

    public void onData(int id) {
        // ringBuffer获取下一个 槽位
        long sequence = ringBuffer.next();

        //获取空的事件,这样就可以复用事件,减少GC;每个槽位对应固定的一个事件
        OrderEvent orderEvent = ringBuffer.get(sequence);
        //业务数据的封装
        orderEvent.setId(id);

        //发布事件
        ringBuffer.publish(sequence);
        System.out.println(Thread.currentThread().getName()+"事件已发布:"+id);
    }
}


//Main运行
public class DisruptorDemo {
    public static void main(String[] args) throws InterruptedException {
        //提供一个线程池,里面的线程作为消费者(Consumer)的事件处理线程
        ExecutorService threadPool = Executors.newFixedThreadPool(4);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        //创建事件工厂(业务数据载体工厂)
        OrderEventFactory orderEventFactory = new OrderEventFactory();
        //创建环形数组的大小,一定是2的指数次幂
        int ringBuffferSize = 1024;
        //创建Disrutor
        Disruptor<OrderEvent> disruptor = new Disruptor<OrderEvent>(orderEventFactory, ringBuffferSize,
                threadPool, ProducerType.MULTI, new YieldingWaitStrategy());

        //设置事件处理器,也就是消费者,这里设置了两个消费者,一个线程要对应一个消费者,同一个事件被两个消费者消费
        EventHandlerGroup<OrderEvent> handlerGroup = disruptor.handleEventsWith(new OrderEventHandler(), new OrderEventHandler());
       //这个设置前面两个消费者消费完之后,再消费
        //这里可以设置 消费顺序,还有after()定义更灵活的先后顺序
        handlerGroup.then(new OrderEventHandler());

        //启动disruptor
        disruptor.start();

        //获取ringBuffer 环形缓冲区,其内部存储的结构是数组
        RingBuffer<OrderEvent> ringBuffer = disruptor.getRingBuffer();
        //创建自定义的生产者,其实就是传入ringBuffer,然后往里面添加数据
        OrderEventProducer eventProducer = new OrderEventProducer(ringBuffer);

        //添加数据到生产者中进行发布
        for (int i = 1; i < 3; i++) {
            eventProducer.onData(i);
        }
        Thread.sleep(1000);
        threadPool.shutdown();
        disruptor.shutdown();

    }
}

结果为:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值