zoukankan      html  css  js  c++  java
  • disruptor 笔记

    Disruptor 为什么快?

    原文学习地址http://ifeve.com/locks-are-bad/ 

    情景前提:

    并发   --  两个线程同时尝试修改同一个变量。

    无锁情况,变量值为后修改值。且可能造成线程数据错误。

    加锁:悲观锁  -  某一线线程获取变量值,直到它释放锁,另一线程才能获取变量值。 == 锁阻塞,线程越多,系统响应越慢。

        乐观锁  -  某一线程获取变量值,不锁定,当修改值时才锁定。如果修改时变量值已经被修改,则重新获取变量值,再做修改。

    死锁: 两个线程需要访问两个相同变量,都在锁定不同的一个变量后去获取另一个变量。

    Disruptor 为什么高效率?为什么快?实现方式:

       Disruptor 不用锁

    在线程安全地方,用CASCompare And Swap/Set  比较并交换)操作,CPU 级指令【类似于乐观锁,cpu更新一个值,如果要更改的值被改变,操作失败。】,CAS操作比锁消耗资源小很多【因为不牵扯操作系统】。

    Disruptor ClaimStrategy(索赔策略)中,有另个策略:SingleThreadedStrategy 单线程策略,MultiThreadedStrategy 多线程策略。

    单线程策略用 long,多线程策略用 AtomicLong (原子 long,是加了synchronized long

    重点:在整个复杂框架中,只有一个地方出现多线程竞争修改同一个变量值。

    所有访问对象都拥有序号,多线程唯一会被线程竞争写入的序号就是ClaimStrategy对象中的那个。

    Disruptor 确保了Entry中每个变量都只被一个消费者写,没有写竞争,就不需要锁 或 CAS

    总结:Disruptor 优点

    1、没有竞争 - 没有锁 - 非常快

    2、访问者记录自己的序号实现方式,允许多个生产者与多个消费者共享相同数据结构

    3、每个对象都能跟踪序列号,没有伪共享和非预期竞争。

    Disruptor的目标是尽可能多的在内存中运行 ,并对缓存行进行填充

    原文学习地址http://ifeve.com/disruptor-cacheline-padding/ 

    CPU 到内存之间有好几层缓存。L1 一级缓存,L2 二级缓存, L3 三级缓存。

    时间消耗对比:

    CPU

    CPU周期

    时间

    主存

    60 - 80 纳秒

    L3 cache

    40 - 45 cycles 周期

    15 ns

    L2 cache

    10 cycles周期

    3 ns

    L1 cache

    3 - 4 cycle 周期

    1 ns

    寄存器

    1 cycle 周期

    缓存是由缓存行组成,通常是 64 字节( 2 的整数幂个连续字节 )。(例:Java long 类型是 8字节,一个缓存行可以存 8 long 类型的变量。),内存读取每次会读取一个缓存行,然后对行内数据进行循环读取,期间会无效掉非本线程的数据并重新读取。

    伪共享:两个线程下两个不同的变量被存储在同一个缓存行。单个线程写变量时会把整个缓存行都读取出来,然后无效掉非变量的数据,再重新读取。

    Disruptor 数据存储会对缓存行进行填充。通过增加补全来确保 ring buffer 的序列号不会和其他东西同时存在于一个缓存中。没有伪共享,没有和其他变量的意外冲突。( 如果访问一个 long 数组,当数组中一个值被加载到缓存中,它会额外加载另外 7个,这样可以非常快速便利在连续内存块中分配的任意数据结构。)

    :降低 内存屏障 使用

    原文学习地址http://ifeve.com/disruptor-memory-barrier/ 

    内存屏障 是一个 CPU 指令,作用:

     确保一些特定操作执行的顺序

     影响一些数据的可见性(可能是某些指令执行后的结果)。

    编译器 CPU 可以在保证输出结果一样的情况下对指令重排序,使性能得到优化。插入一个内存屏障后,先于这个命令的必须先执行,后于这个命令的必须后执行。即 强制更新一次不同 CPU 的缓存。

    Java 中的 Volatile 关键字 (内存屏障)。

    使用 volatile 修饰的变量会强制将修改的值 立即写入主存。主存中值更新会使缓存中值失效。

    Volatile 具有 可见性、有序性。   没有原子性【 Volatile 没有原子性,是于 synchronized Lock 最大的功能差异。】。

    JDK volatile 应用: ConcurrentHashMap Entry 中的 value next  被声明为 volatile

    AtomicLong 中的 value 被声明为 volatile

    内存屏障有开销(编译器/CPU 不能重拍指令,导致不能最搞笑利用 CPU,另外刷新缓存也有开销),Disruptor 的实现对序列号(多线程中序列号是 AtomicLong类型的)的读写频率尽量降到最低。

  • 相关阅读:
    GO语言的进阶之路-Golang字符串处理以及文件操作
    将本地的代码推送到公网的github账号去
    GO语言的进阶之路-go的程序结构以及包简介
    Linux操作系统原理
    Linux安装-kickstart无人值守安装
    LVM逻辑卷管理
    Liunx软Raid实现
    parted分区工具用法
    高级Linux运维工程师必备技能(扫盲篇)
    H3C配置FTP服务器
  • 原文地址:https://www.cnblogs.com/wgy1/p/10754989.html
Copyright © 2011-2022 走看看