55 | 理解Disruptor(下):不需要换挡和踩刹车的CPU,有多快?
55 | 理解Disruptor(下):不需要换挡和踩刹车的CPU,有多快?
讲述:徐文浩
时长08:31大小7.80M
缓慢的锁
无锁的 RingBuffer
总结延伸
推荐阅读
课后思考
赞 19
提建议
精选留言(25)
- 唐朝农民2019-09-09ReentrantLock内部不也是CAS实现的吗
作者回复: 并不是,ReentrantLock内部只是有CAS方法的调用而已,而这个调用是为了做状态检查和获取锁。 ReetrantLock最终会调用AbstractQueuedSynchronizer,并且会在拿不到锁的时候会有让当前节点sleep的过程。 这个可以深入读一下ReetrantLock的源码。 本质上,ReentrantLock等于是在Java层面实现了一个跨平台的锁机制,它的底层调用用到了CAS方法而已。而不是直接基于CAS来设计队列。
22 - 姜戈2019-09-09课后问题: 对比了Sequence与AtomicLong源码,在于CAS之前,Disruptor直接取原有值,做CAS操作;而AtomicLong使用了getLongVolatile获值。 两者value都用了Volatile来修饰,这点是共同的;但在于获值时,AtomicLong又再次使用volatile(getLongVolatile), 由于Volatile会在内存中强行刷新此值,相比这样就会增加一次性能的消耗,另外还有Spinlock(自旋锁),这两点都会带来性能消耗。 为什么Disruptor可以这样做,免掉getLongVolatile操作,我猜想应该是缓存行的填充,避免了伪共享(False Sharing)的问题,线程对value值的修改,不会出现不同线程同时修改同一缓存行不同变量的问题,所以不需要再次强行刷内存的步骤。 请老师指正!!! /** Sequence关于取值是直接Get(), 查看内部方法直接返回value * * Atomically add the supplied value. * * @param increment The value to add to the sequence. * @return The value after the increment. */ public long addAndGet(final long increment) { long currentValue; long newValue; do { currentValue = get(); newValue = currentValue + increment; } while (!compareAndSet(currentValue, newValue)); return newValue; } /** AtomicLong关于取值时使用getLongVolatile,查看其源码,增加了Volatile和自旋锁 * * getAndAddLong * */ public final long getAndAddLong(Object var1, long var2, long var4) { long var6; do { var6 = this.getLongVolatile(var1, var2); } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4)); return var6; } /** getLongVolatile源码(C++) * * 1. volatile, 刷新内存 * 2.Spinlock, 自旋锁 */ jlong sun::misc::Unsafe::getLongVolatile (jobject obj, jlong offset) { volatile jlong *addr = (jlong *) ((char *) obj + offset); spinlock lock; return *addr; }展开
作者回复: 抱歉,不过你的 AtomicLong 部分的代码时从哪里看到的?似乎和open jdk里面的源码完全不一样。 Sequence主要是做了Cache Line Padding,来解决False Sharing的问题,和AtomicLong比较,避免Cache里面的数据被反复交换到内存里面去。 “ In fact the only real difference between the 2 is that the Sequence contains additional functionality to prevent false sharing between Sequences and other values.”
共 3 条评论20 - 活的潇洒2019-09-15极客时间买了13个专栏,终于完成了一个专栏的所有笔记 专栏所有笔记如下:https://www.cnblogs.com/luoahong/p/10496701.html
作者回复: 👍不容易
共 7 条评论12 - 南山2019-09-09专栏最开始就订阅了,看到了这篇专栏目录中的这篇文档标题,当时就想着要从底层了解disruptor的原理,也一篇一篇认真的跟了下来,不知不觉就到了今天,虽然有很多后续还是要回去时常翻阅的知识点,但是也庆幸自己能一路跟着老师到现在。 课后思考题会去翻源码再回来补充
作者回复: 👍 Disruptor的代码短小精干,很适合深入读一读
8 - 姜戈2019-09-09LinkedBlockingQueue源码(JDK1.8)看了一下, 使用的是ReentrantLock和Condition来控制的,没发现老师说的synchronized关键字, 是不是哪里我忽略掉了?
作者回复: 是我笔误了,是ReetrantLock,下面我给的对比示例也是ReentrantLock的。
5 - 分清云淡2021-06-07文章中加锁和不加锁性能比较的case老师的数据是差了50倍,我在intel E5 v4和鲲鹏920下跑出来都是查了80倍。 不过其中的原因不是因为加锁,而是lock.lock() 函数自身包含了几十条指令,而++只有简单的几个指令,也就是两者指令数就差了1个数量级。 例子中不加锁性能好的另外一个原因是 ++指令 对流水线十分友好,基本能跑满流水线(IPC 跑到4);而lock()那堆指令只能把IPC跑到0.5-0.8之间。 在只有一个线程的情况下加锁中的"锁"对性能是没有任何影响的,比如假设你的业务逻辑几万个指令,加上lock()多出来的几十个指令基本,基本是没有啥影响的。只有一个线程不会真正发生切换、等锁的糟心操作。只需要考虑实际指令量和对pipeline的友好程度展开共 1 条评论4
- 大头爸爸2020-03-31runIncrementWithLock()里面对每个counter++执行lock和unlock操作,可是就只有一个线程用到这个reentrantlock,那么这个lock()和unlock()应该花不了多少时间啊,也不会有多少context switch操作啊?3
- D2019-09-09看了下jdk 1.8的 AtomicLong 和disruptor的sequence, 个人认为有两点不同: 1. disruptor 加了上一讲里面的padding, 以进一步利用缓存。 2. Atomiclong 里面提供了更多的方法,并且判断cpu是否支持无锁cas 相同点,底层都是调用unsafe类实现硬件层面的操作,以跳过jdk的限制。
作者回复: 👍理解准确到位
4 - leslie2019-09-09学习中一步步跟进:越来越明白其实真正弄明白为何学习的中间层其实是操作系统和计算机原理;就像老师的课程中不少就和操作系统相关,而在此基础上可以衍生出一系列相关的知识。 就像老师的2个TOP:存储与IO系统,应用篇分别就是一个向下一个向上;代码题对于不做纯代码超过5年的来说有点难,跟课再去学Java有点难且时间实在不够-毕竟运维的coding能力都偏差,不过大致能明白原理,从原理层去解释一些问题的根源原因,以及某些场合下那些可能更加适用。 其实通过操作系统和组成原理会衍生出很多知识:选择合适的方向专攻确实不错。向老师提个小的要求:希望在最后一堂课可以分享老师在做这个专栏所查阅的主要书籍,毕竟没人可以学完一遍就基本掌握的;老师所看的核心资料是我们后续再看课程提升自己的一个辅助手段吧。展开
作者回复: 从运维的角度,其实还有一个重要的主题是网络,也值得仔细关注一下。 在专栏的每篇文章后面列了推荐阅读,也在一开始的学习方法的那一讲里面列了一些参考资料。可以先从这些看起。 另外一个去处是去看Wikipedia对应的条目以及相关引用。
共 2 条评论3 - 免费的人2019-09-11Cas仍然会涉及到内存读取,老师能否介绍下rcu锁?
作者回复: 是的,CAS仍然会涉及到内存读取。 我之前没有仔细了解过RCU,今天简单看了一下资料,如果我的理解没有错的话。作为读写锁替代的话,是OK的。不过在Disruptor的场景下,Consumer需要的并不是一个读写锁,Producer和Consumer因为都需要更新自己的位置,其实是两个写锁。我直觉上觉得并没有办法依靠RCU的方式来解决。 不过这个值得仔细想一下。
2 - 逍遥法外2019-09-09ReentrantLock内部也有CAS的操作,只不过也会有Lock的操作。Disruptor直接使用CAS,是简化了操作。
作者回复: 👍 ReetrantLock里面的CAS是作为调用方式,最终来实现一个JVM上跨平台的锁的
2 - 陈冰清2021-07-20老师 你有没有兴趣去量化私募行业做CTO呢 或者架构系统优化之类的岗位呢?1
- Monday2020-06-20how 激动 ,第一遍花了大半年,终于撸完了共 2 条评论1
- 信长2020-03-26你说到序号的时候,我以为假如有两个消费者,这两个消费者一个按着奇数的序列号消费,另一个按着偶数消费,类似这种思维,想多了吗1
- 曙光2019-10-22compxchg [ax] ,[bx], [cx]和 UNSAFE.compareAndSwapLong(this, VALUE_OFFSET, expectedValue, newValue)d对应关系没搞明白。jdk源码到compareAndSwapLong,只有 @HotSpotIntrinsicCandidate public final native boolean compareAndSetLong(Object o, long offset,long expected,long x); 不知道compxchg的[ax] ,[bx], [cx]和compareAndSetLong方法有什么对应关系展开1
- JaredLuo2022-05-14老师推荐阅读里的各种英文论文资料,这些论文资料都是去哪里找的?
- Geek_337e212021-09-07老师,有没有C++代码的例子啊?
- Ins2021-07-25Ringbuffer无锁化没明白,compareAndSet可以线程安全执行,但是外面的newValue = currentValue + increment;语句(或实际的生产消费代码)不还是会出现多线程竞争吗?
- 文进2021-06-01老师,此处Java锁的例子不太准确,因为你是在单线程下获取以及释放锁,其实并没有产生锁竞争,并不会有线程上下文切换问题。所以对吧cas的程序差距不大,如果改成几个线程一起执行,抢夺一个锁,效果会更明显
- 曾泽浩2021-04-07老师,锁在硬件层面的实现有相关资料推荐吗?