• Disruptor(一)Sequence


    在大扎了解Disruptor的过程中,根据各个组件的的依赖程度,分别按照以下顺序来分篇说明每个组件的作用和用法,在下面的过程中,会以源码为主,手册为辅的方式,描述各个组件的作用以及原因。

    Sequence -> Sequencer-> Sequence Barrier -> Event -> Producer -> Ring Buffer -> Event Processor -> Wait Strategy -> Event Handler

    1. 前言

    并发sequence是用来进行ring buffer与event处理者情况进行跟踪的,支持大量CAS和顺序写的并发操作。

    并且通过对volatile字段周围填充的方式对伪共享问题更有效果,

    2. 方法(一致性保证)

    使用了一个VarHandle句柄来保证访问变量数据的并发操作,涉及到了sequence的3个方法:

    compareAndSet

    调用了VarHandle的compareAndSet方法进行操作。

    1. /**
    2.  * Perform a compare and set operation on the sequence.
    3.  *
    4.  * @param expectedValue The expected current value.
    5.  * @param newValue      The value to update to.
    6.  * @return true if the operation succeeds, false otherwise.
    7.  */
    8. public boolean compareAndSet(final long expectedValue, final long newValue)
    9. {
    10.     return VALUE_FIELD.compareAndSet(this, expectedValue, newValue);
    11. }

    incrementAndGetaddAndGet

    实际调用了VarHandle的getAndAdd方法进行操作,这里sequence的方法是add->get,而VarHandle的方法是get->add,所以在这个方法里面把调用VarHandler的值又加了个递增值。

    1. /**
    2.  * Atomically add the supplied value.
    3.  *
    4.  * @param increment The value to add to the sequence.
    5.  * @return The value after the increment.
    6.  */
    7. public long addAndGet(final long increment)
    8. {
    9.     return (long) VALUE_FIELD.getAndAdd(this, increment) + increment;
    10. }

    在VarHandle的这些操作方式里面,访问模式会覆盖掉定义时声明的模式,也就是说,如果你声明了一个反射字段句柄,但你使用了普通变量句柄来访问,会导致访问模式变为普通变量的模式。

    其他方法

    在其余的几个get(),set(),setVolatile()方法内均使用了VarHandle

    插入内存屏障的方法来保证数据的一致性。

    3.缓存行填充

    在Sequence中有一个内部类LhsPadding,里面声明了一堆变量,看起来是这样的:

    1. class LhsPadding
    2. {
    3.     protected byte
    4.         p10, p11, p12, p13, p14, p15, p16, p17,
    5.         p20, p21, p22, p23, p24, p25, p26, p27,
    6.         p30, p31, p32, p33, p34, p35, p36, p37,
    7.         p40, p41, p42, p43, p44, p45, p46, p47,
    8.         p50, p51, p52, p53, p54, p55, p56, p57,
    9.         p60, p61, p62, p63, p64, p65, p66, p67,
    10.         p70, p71, p72, p73, p74, p75, p76, p77;
    11. }

    这一堆变量里面,有8*7=56个byte,而在还有个内部类Value继承了LhsPadding,

    整体结构这样:

     

    并且还有个内部类RhsPadding又集成了Value,并且他也恰好有8*7=56个byte,而Value的数据类型是long,long为8个字节,所以这个Sequence对象一共有56+8+56=120个byte,而一般缓存行大小是64字节,无论如何分割,这个8个字节的byte都会与左侧填充或者右侧填充达到64个字节,从而避免伪共享问题。

                                            L                        Value                             R   

    56 byte8 byte56 byte
    56 byte8 byte56 byte

  • 相关阅读:
    软考高级系统架构设计师系列之:案例分析典型试题四
    Vue框架学习笔记——v-bind数据单向绑定和v-model数据双向绑定
    猿创征文 | Linux运维工程师的10个日常使用工具分享
    python实现科研通定时自动签到
    在面试中,如何回复擅长 Vue 还是 React
    iOS 17 测试版中 SwiftUI 视图首次显示时状态的改变导致动画“副作用”的解决方法
    TypeScript精华精讲(接口与泛函)
    ansible运维自动化之playbook
    线性同余方程( 数学知识 + 同余 + 扩展欧几里得算法 )
    【附源码】计算机毕业设计JAVA婚纱摄影管理
  • 原文地址:https://blog.csdn.net/pxg943055021/article/details/125403317