Java并发编程实战
王宝令
资深架构师
立即订阅
15141 人已学习
课程目录
已完结 50 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 你为什么需要学习并发编程?
免费
学习攻略 (1讲)
学习攻略 | 如何才能学好并发编程?
第一部分:并发理论基础 (13讲)
01 | 可见性、原子性和有序性问题:并发编程Bug的源头
02 | Java内存模型:看Java如何解决可见性和有序性问题
03 | 互斥锁(上):解决原子性问题
04 | 互斥锁(下):如何用一把锁保护多个资源?
05 | 一不小心就死锁了,怎么办?
06 | 用“等待-通知”机制优化循环等待
07 | 安全性、活跃性以及性能问题
08 | 管程:并发编程的万能钥匙
09 | Java线程(上):Java线程的生命周期
10 | Java线程(中):创建多少线程才是合适的?
11 | Java线程(下):为什么局部变量是线程安全的?
12 | 如何用面向对象思想写好并发程序?
13 | 理论基础模块热点问题答疑
第二部分:并发工具类 (14讲)
14 | Lock和Condition(上):隐藏在并发包中的管程
15 | Lock和Condition(下):Dubbo如何用管程实现异步转同步?
16 | Semaphore:如何快速实现一个限流器?
17 | ReadWriteLock:如何快速实现一个完备的缓存?
18 | StampedLock:有没有比读写锁更快的锁?
19 | CountDownLatch和CyclicBarrier:如何让多线程步调一致?
20 | 并发容器:都有哪些“坑”需要我们填?
21 | 原子类:无锁工具类的典范
22 | Executor与线程池:如何创建正确的线程池?
23 | Future:如何用多线程实现最优的“烧水泡茶”程序?
24 | CompletableFuture:异步编程没那么难
25 | CompletionService:如何批量执行异步任务?
26 | Fork/Join:单机版的MapReduce
27 | 并发工具类模块热点问题答疑
第三部分:并发设计模式 (10讲)
28 | Immutability模式:如何利用不变性解决并发问题?
29 | Copy-on-Write模式:不是延时策略的COW
30 | 线程本地存储模式:没有共享,就没有伤害
31 | Guarded Suspension模式:等待唤醒机制的规范实现
32 | Balking模式:再谈线程安全的单例模式
33 | Thread-Per-Message模式:最简单实用的分工方法
34 | Worker Thread模式:如何避免重复创建线程?
35 | 两阶段终止模式:如何优雅地终止线程?
36 | 生产者-消费者模式:用流水线思想提高效率
37 | 设计模式模块热点问题答疑
第四部分:案例分析 (4讲)
38 | 案例分析(一):高性能限流器Guava RateLimiter
39 | 案例分析(二):高性能网络应用框架Netty
40 | 案例分析(三):高性能队列Disruptor
41 | 案例分析(四):高性能数据库连接池HiKariCP
第五部分:其他并发模型 (4讲)
42 | Actor模型:面向对象原生的并发模型
43 | 软件事务内存:借鉴数据库的并发经验
44 | 协程:更轻量级的线程
45 | CSP模型:Golang的主力队员
结束语 (1讲)
结束语 | 十年之后,初心依旧
用户故事 (2讲)
用户来信 | 真好,面试考到这些并发编程,我都答对了!
3 个用户来信 | 打开一个新的并发世界
Java并发编程实战
登录|注册

40 | 案例分析(三):高性能队列Disruptor

王宝令 2019-05-30
我们在《20 | 并发容器:都有哪些“坑”需要我们填?》介绍过 Java SDK 提供了 2 个有界队列:ArrayBlockingQueue 和 LinkedBlockingQueue,它们都是基于 ReentrantLock 实现的,在高并发场景下,锁的效率并不高,那有没有更好的替代品呢?有,今天我们就介绍一种性能更高的有界队列:Disruptor。
Disruptor 是一款高性能的有界内存队列,目前应用非常广泛,Log4j2、Spring Messaging、HBase、Storm 都用到了 Disruptor,那 Disruptor 的性能为什么这么高呢?Disruptor 项目团队曾经写过一篇论文,详细解释了其原因,可以总结为如下:
内存分配更加合理,使用 RingBuffer 数据结构,数组元素在初始化时一次性全部创建,提升缓存命中率;对象循环利用,避免频繁 GC。
能够避免伪共享,提升缓存利用率。
采用无锁算法,避免频繁加锁、解锁的性能消耗。
支持批量消费,消费者可以无锁方式消费多个消息。
其中,前三点涉及到的知识比较多,所以今天咱们重点讲解前三点,不过在详细介绍这些知识之前,我们先来聊聊 Disruptor 如何使用,好让你先对 Disruptor 有个感官的认识。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Java并发编程实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(28)

  • Juc
    希望老师解释下,为什么创建元素的时间离散会导致元素的内存地址不是连续的?这些元素不是存在数组中的吗?数组初始化不是已经连续分配内存了吗?

    作者回复: 数组连续,数组里只有引用,e1 e2这些对象的地址不连续

    2019-05-30
    1
    11
  • 孙志强
    程序局部性原理的空间局部性是不是cpu分支预测?缓存行一般是64字节,takeIndex那为何前后填充56个字节,大于64了,怎么独占缓存行?
    2019-05-30
    1
    8
  • disruptor高性能主要是以下几点设计:
    1,仅维护一个共享变量(入队索引),减少锁竞争,并利用填充行技术解决共享变量的伪共享问题。
    2,底层使用循环数组作为存储结构,开辟一组连续的内存空间,循环利用减少gc次数,并充分利用了程序局部性原理。
    3,入队时支持一次性获取多个索引,然后在当前线程写入数据,减少锁竞争,消费时一样。
    不知道我理解的对不对?
    2019-05-30
    4
  • LW
    RingBuffer是一个环形队列?
    2019-05-30
    4
  • 冰激凌的眼泪
    前后56个字节保证了目标字段总是独占一个cache line,不受周围变量缓存失效的影响

    作者回复: 👍

    2019-07-03
    3
  • 郑晨Cc
    全他妈的是干货 满足!
    2019-06-05
    2
  • 爱吃回锅肉的瘦子
    难度指数提升😔只能得多看几遍
    2019-06-01
    2
  • 晓杰
    mysql也利用了程序的局部性原理来减少磁盘的io,百度开源的分布式唯一id生成器也使用了RingBuffer,将提前生成的id缓存到RingBuffer中。
    2019-05-30
    1
  • 张三
    打卡!
    2019-05-30
    1
  • 放个屁臭到了自己
    “ArrayBlockingQueue 的入队和出队操作是用锁来保证互斥的,所以入队和出队不会同时发生。如果允许入队和出队同时发生,那就会导致线程 A 和线程 B 争用同一个缓存行,这样也会导致性能问题。所以为了更好地利用缓存,我们必须避免伪共享,那如何避免呢?”

    JMM关于synchronized的两条规定:
      1)线程解锁前,必须把共享变量的最新值刷新到主内存中
      2)线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新获取最新的值

    这样来看,由于 cpu 读取数据是按照缓存行读取的,那么虽然加锁了,但还是会按照缓存行读取啊,如果我加锁后访问了多个变量,后面依然会全部读取进来吧。

    这种情况不是也是伪共享嘛??依然是有性能问题的吧。。。

    那对于加锁的情况,使用缓存行填充是否也可以优化性能呢?
    2019-11-26
  • 放个屁臭到了自己
    ArrayBlockingQueue 的入队和出队操作是用锁来保证互斥的,所以入队和出队不会同时发生。如果允许入队和出队同时发生,那就会导致线程 A 和线程 B 争用同一个缓存行,这样也会导致性能问题。

    想问下加锁了就没有缓存干扰了吗!为啥?
    2019-11-26
  • aoe
    第一次知道伪共享
    2019-11-22
  • xinglichea
    老师,感觉填充的模式不是很靠谱,程序的健壮性要强依赖于CPU的缓存行的实现,打个比如,如果以后CPU缓存行变成了128个字节,那企不要写Disruptor的实现源码,然后原来实现的代码仍然会有伪共享的问题!!!

    作者回复: 我觉得从官方提供注解这一行为来看,应该不至于不靠谱,不过java程序员都习惯于不关注硬件。

    2019-08-29
  • neohope
    有个地方没看懂,if (wrapPoint>cachedGatingSequence || cachedGatingSequence>current),这个条件里面,为何需要cachedGatingSequence>current这个限制呢?
    是当current突破最大值变为0之后,要等到cachedGatingSequence追上来才继续生产吗?
    2019-08-18
  • 青铜5 周群力
    对内存优化部分有质疑:
    1申请内存要经过jvm,经过操作系统,经过层层的优化、内存管理,填充缓存行有没有用只靠逻辑推理是推理不出来的吧,有没有数据证明这个技术真的有用?
    2.预先申请所有元素对象真的有用吗,因为每个元素引用的数据对象还在离散的内存空间,取数据对象还要访问内存,会发生cache line淘汰。所以有什么数据能证明这个技术有用吗
    2019-07-31
  • 小予
    关于第4点,批量消费,一个线程一次读取n个元素,那另外一个线程想要读取元素时,必须等前一个线程的n个元素读取完,不明白这样为何能提高性能,希望老师解答下
    2019-07-30
  • 空知
    老师问下
    缓存行填充之后,缓存行里加载的不是真实需要的数据 是填充数据 程序局部性会不会不适用了?

    作者回复: 适用,只是避免一个缓存行内互相干扰而已

    2019-06-09
  • nico
    老师,问下,多个生产者同时生产时,如果前面申请成功但是生产失败了,后面的生产成功了,中间空出来的位置怎么处理?
    2019-06-07
  • 在下令狐冲
    缓存行填充可以看看这篇文章,简单明了
    http://ifeve.com/disruptor-cacheline-padding/
    2019-06-03
  • 码农Kevin亮
    老师,避免伪共享的逻辑有点困惑:
    伪共享逻辑上就是没实现共享,而disruptor用行填充也是没实现共享。那么为什么避免伪共享就能提升性能呢?

    作者回复: 共享,指的是多个核能共享缓存,避免伪共享后,多个核是可以共享缓存的

    2019-06-02
收起评论
28
返回
顶部