关于java:学习Disruptor时的一些联想和总结

56次阅读

共计 2436 个字符,预计需要花费 7 分钟才能阅读完成。

一、前言

  前两天整 log4j2 的时候不是碰到 Disruptor 了嘛,想着能不能从这个吹爆了的并发框架里吸取些性能优化的思路呢。略微学习下源码,还真是有播种的。

对 Disruptor 感兴趣的小伙伴能够看看哈希大佬的博客,对于 Disruptor 有系列文章,算是网上比拟细的https://blog.csdn.net/zhxdick…

二、RingBuffer

  大抵看了下,如同所有的点都和 RingBuffer 无关,就将题目写成 RingBuffer。

  1. Disruptor 在初始化的时候创立指定大小的 RingBuffer,创立之后大小恒定,不会主动扩容,防止了 频繁扩容 的性能损耗;【能够联想到 List、Map、StringBuilder、StringBuffer 些主动扩容的骚玩意儿】
     
  2. RingBuffer中存放数据的外围是 entries 的数组变量,RingBuffer初始化的时候会进行 数据预调配 【是不是相似一些池化的货色】,将数组填满;【数据内容为自定义的 event 对象】。这段逻辑在 RingBuffer 的父类RingBufferFields 实现中能够看到:
RingBufferFields(
    EventFactory<E> eventFactory,
    Sequencer sequencer)
{
    this.sequencer = sequencer;
    this.bufferSize = sequencer.getBufferSize();
    if (bufferSize < 1)
    {throw new IllegalArgumentException("bufferSize must not be less than 1");
    }
    if (Integer.bitCount(bufferSize) != 1)
    {throw new IllegalArgumentException("bufferSize must be a power of 2");
    }
    this.indexMask = bufferSize - 1;
    this.entries = new Object[sequencer.getBufferSize() + 2 * BUFFER_PAD];
    // 预调配对象
    fill(eventFactory);
}
private void fill(EventFactory<E> eventFactory)
{for (int i = 0; i < bufferSize; i++)
    {entries[BUFFER_PAD + i] = eventFactory.newInstance();}
}
  1. RingBuffer相比拟其余环形数据结构,基于 数组 实现【能够充分利用CPU cache line,高性能】,没有尾指针,只有一个 next 指针,指向下一个可用地位。
     
  2. RingBuffer循环写的核心思想是,应用 sequence 的下标和 bufferSize 进行取模运算,理论应用的位运算【联想到 HashMap 的数组定位】,在二进制环境下,位运算 mod运算性能高很多。
     
  3. RingBuffer不会被动革除曾经生产过的对象信息,只会笼罩先前的数据,而笼罩操作其实是自定义 event 对象属性的笼罩。这里实现了 对象重用,升高了 GC 压力
    对象重用在某些中央对性能晋升几乎就是腾飞模式,特地对于那些 频繁初始化的重类 比方 xstream,或者可能会波及类加载这种耗时操作的,尽可能应用 单例模式 或者 对象池 进行操作。
     
  4. Disruptor 充分利用了 CPU cache line 的弱小性能,为了 解决伪共享 的问题,在频繁应用的 Sequence 类和 RingBuffer 类中,能够看到很多用来填充对齐的 long 变量;Sequence 在解决的时候在左右别离设置 7 位 padding long【缓存填充数据】保障独占缓存行;
class LhsPadding
{protected long p1, p2, p3, p4, p5, p6, p7;}

class Value extends LhsPadding
{protected volatile long value;}

class RhsPadding extends Value
{protected long p9, p10, p11, p12, p13, p14, p15;}

同样的操作在 RingBuffer 中:

abstract class RingBufferPad
{protected long p1, p2, p3, p4, p5, p6, p7;}

abstract class RingBufferFields<E> extends RingBufferPad
{
    private static final int BUFFER_PAD;
    private static final long REF_ARRAY_BASE;
    private static final int REF_ELEMENT_SHIFT;
    private static final Unsafe UNSAFE = Util.getUnsafe();
    ....
}

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;
    ....
}
  1. 还有就是 Disruptor 的大思维,无锁化 。Disruptor 中,除了WaitStrategy 那块波及到 ReentrantLock 外,根本就没有其余锁的操作,要么就是 CAS。在多线程环境下,线程数越多,线程程竞争越强烈,锁的开销越大。这里甚至能够联想到一些 无锁串行化 的利用实现,比方 redis,netty。
    还有相似的其余无锁的框架:java.util.concurrent.atomic包、Amino 框架

结尾

  也没啥说的了,如果对各位小伙伴有些些启发的话,就关注我吧➕。

正文完
 0