Disruptor源码阅读笔记一

Disruptor核心类

阅读代码之前,建议大家先把Disruptor run起来,这样对于这个组件的一些核心概念就了解了,下面首先分析一下disruptor的核心类及其功能:

  • Ring Buffer: disruptor的数据结构,一个环形队列,主要负责放入生产的消息,会在初始化的时候创建好所有event对象,以减少垃圾回收
  • Sequence: 序列器,Sequence本身提供了类似AtomicLong的各个特性。生产者,消费者都是这个类的使用者,每个使用者都会维护一个Sequence来标识自己的读/写下标。,disruptor里面大部分的并发代码都是通过对Sequence的值同步修改实现的,而非锁,这是disruptor高性能的一个主要原因.
  • Sequencer: disruptor里生产同步的实现者,Sequencer有单生产者,多生产者两种不同的模式,里面实现了各种同步的算法
  • Sequence Barrier: Sequence Barrier是由Sequencer创建的,并被Processor持有,主要用来判断Processor是否可以拿到待处理的事件(生产是否到位,前置Processor是否消费完成,Processor是否被停止)
  • Wait Strategy: disruptor提供了多个等待策略,比如阻塞等待唤醒的BlockingWaitStrategy,疯狂压榨cpu自旋追求响应时间的YieldingWaitStrategy(类似于synchronize的重量级锁,和轻量级锁的思路但是没有锁的升级逻辑)。
  • Event: disruptor中传递的事件.
  • EventProcessor/workProcessor: 处理Event的循环,在循环中获取Disruptor的事件,然后把事件分配给各个handler
  • EventHandler: 负责业务逻辑的handler,由使用者自己实现
  • Producer: 生成event事件的生产者

RingBuffer

首先我们先看这个java文件,这里面有个三个类:

  • RingBufferPad
  • RingBufferFields
  • RingBuffer

使用了内存缓冲的优势提升性能

RingBufferPad从名字可以看出,padding英文为“垫料”的意思,所以这个类就是单纯的为了防止“伪共享”而进行内存填充的类。

关于“伪共享”这里引用一下美团技术博客的一个例子来证实下内存填充的高性能:

public class CacheLineEffect {
    //考虑一般缓存行大小是64字节,一个 long 类型占8字节
    static  long[][] arr;
 
    public static void main(String[] args) {
        arr = new long[1024 * 1024][];
        for (int i = 0; i < 1024 * 1024; i++) {
            arr[i] = new long[8];
            for (int j = 0; j < 8; j++) {
                arr[i][j] = 0L;
            }
        }
        long sum = 0L;
        long marked = System.currentTimeMillis();
        // 此处使用的内存空间为:(1024*1024)个大小为(8*8)字节的空间,每个大小为64字节的空间都是连续的。
        for (int i = 0; i < 1024 * 1024; i+=1) {
            for(int j =0; j< 8;j++){
                sum = arr[i][j];
            }
        }
        System.out.println("Loop times:" + (System.currentTimeMillis() - marked) + "ms");
 
        marked = System.currentTimeMillis();
        // 此处使用的内存空间为:(8)个大小为(1024*1024*8)字节的空间,每个大小为(1024*1024*8)字节的空间都是连续的。
        for (int i = 0; i < 8; i+=1) {
            for(int j =0; j< 1024 * 1024;j++){
                sum = arr[j][i];
            }
        }
        System.out.println("Loop times:" + (System.currentTimeMillis() - marked) + "ms");
    }
}

上述例子中,第一次for循环利用了缓存行的优势,第二次没有利用缓存行的优势,执行结果如下所示:

Loop times:16ms
Loop times:90ms

关于伪共享的更多内容请大家自行了解。
再看下RingBuffer类的源代码

```java
abstract class RingBufferPad
{
    protected long p1, p2, p3, p4, p5, p6, p7;
}
public final class RingBuffer<E> extends RingBufferFields<E> implements Cursored, EventSequencer<E>, EventSink<E>
{
    public static final long INITIAL_CURSOR_VALUE = Sequence.INITIAL_VALUE;
    protected long p1, p2, p3, p4, p5, p6, p7;
    ...省略n行
}

有人可能会困惑,为啥这里是7个long呢,CPU缓存行不是8个long吗?
这个地方我也不是很懂,以下是个人见解(还请高人评论区指点):这里是让RingBuffer类的变量在内存中前后各有7个long,这样存储使得RingBuffer类的变量如果缓存在CPU缓存行中,不会因为缓存行前后变量的是失效而被动失效,即重新刷新。

RingBuffer将下标操作抽象为Sequencer

阅读RingBuffer类可以发现,该类的核心属性只有一个entries,用来存储event。所有关于下标的操作全部放到了Sequencer中。这样有什么好处呢?

  • RingBuffer和Sequencer采用组合的方式使用,降低了两者的耦合性,各自提高了内聚性;RingBuffer不用操心Sequencer内部是怎么做的,只要调用其接口就好。Sequencer内部可以写一千种实现,但是只要定义好的接口不变,就不会影响RingBuffer的使用。
  • 将下标操作单独封装一个类,提升了扩展性。目前只有两种Sequencer:SingleProducerSequencer和MultiProducerSequencer,如果以后还有第三种Sequencer,这样设计就可以很好地扩展未来的业务变化。也符合开闭原则(面向接口关闭,面向扩展开放)。

Sequence

关于set和setVolatile方法

    public void set(final long value)
    {
        UNSAFE.putOrderedLong(this, VALUE_OFFSET, value);
    }

set方法使用unsafe的putOrderedLong方法,实现了类似AtomicLong.lazySet。

有一个问题,value的类型为volatile,为啥还要有putLongVolatile方法呢?

class Value extends LhsPadding
{
    protected volatile long value;
}
...省略
public void setVolatile(final long value)
{
    UNSAFE.putLongVolatile(this, VALUE_OFFSET, value);
}

value字段可以利用volatile内存屏障的特性来实现volatile写,这里为啥还要调用UNSAFE.putLongVolatile方法呢?这个问题百思不得其解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值