Z001-LongAdder为何出现,它的原理是什么?
2023-01-17 16:29:34 0 举报
详细描述了LongAdder的原理等,以及它为何出现的原因
作者其他创作
大纲/内容
变量BASE
Cell-1
填充字节
LongAdder 出现原因
Cell-2
AtomicInteger、AtomicLong的弊端
要搞清楚AtomicInteger、AtomicInteger的弊端,先要弄明白,它们干了什么事情;要搞清楚干了什么事情,即 AtomicInteger、AtomicInteger的原理;要明白原理,那么最简单、粗暴、也高效地方式就是查阅源码啦,接下来,让我们走进源码
CPU-1
CPU-3
缓存行
源码查看,先看看LongAdder的继承结构:LongAdder -> Striped64 -> span style=\"font-size: inherit;\
变量A
Cell-0
AtomicInteger,会造成很多个线程,并发争抢着修改变量A,但是只能有一个线程去修改,其它线程就会空转
至此,明白了AtomicInteger的原理后,再来分析一下它的弊端;也是CAS的弊端随着CPU的核心数增多,大量的线程在等待着获取执行cmpxchg指令的锁,但是只能一个线程陷入进去,那么其它线程只能空转,可能造成CPU飙百的现象,这就是CAS的弊端,也是AtomicInteger的弊端;那么如何规避这个弊端呢?这要了解lock指令的原理了,根据查阅Intel开发手册描述:所以得出结论:原子性操作加上LOCK指令前缀:1、总线锁保证原子性2、数据已在缓存行中,通过缓存一致性协议(MESI)保证其原子性,PS:一定是数据已在缓存行中,才会是缓存行锁,否则会退化为总线锁的这就是LOCK指令的原理通过了解了lock指令的原理,AtomicInteger实现流程,就可以画出在CPU、内存、缓存行层面的示意图,如下所示:
既然,明白了这些推理过程,就需要验证所想,于是,去看LongAdder的实现过程让我们愉快地看看它的源码吧
CPU-2
既然要分散到缓存行,那么就需要将缓存行填充
将分散的变量,抽象为Cell类,并使用@sun.misc.Contended注解,进行缓存行填充,将变量分散到了不同缓存行上,每个线程有个随机种子-哈希值,去打散到数组中,这种操作就是锁分散操作,提高并行度,将原来很多的线程,分散到不同缓存行上CAS操作,而这些变量分出来多少个合适呢?默认两个,然后再不超过最大值时,会进行扩容操作,若达到了最大值后,当前线程的竞争 分派到其它的cell中
是由MESI协议保证一致性
如何解决这个弊端呢?下面来分析
由于弊端是多个线程去竞争一个变量A的修改,那么我们将变量A分为多个变量,然后打散到缓存行中,这样,多个线程获取不到一个变量的CAS操作,就会去竞争其它的变量的CAS操作,巧妙地分散了注意力,提高了并行度,那么这个值如何统计呢?其实很简单,就是将变量A与分出去的N个变量相加即可,这就是LongAdder出现的原因,如下图,将展示LongAdder 的实现流程的示意图
收藏
0 条评论
下一页