Juc16_LongAdder引入、原理、Striped64、分散热点思想、深度解析LongAdder源码、LongAdder和AtomicLong区别(二)

简介: ③. LongAdder为什么这么快呢?(分散热点)④. 源码解析 longAdder.increment( )①. add(1L)

③. LongAdder为什么这么快呢?(分散热点)


①. LongAdder在无竞争的情况,跟AtomicLong一样,对同一个base进行操作,当出现竞争关系时则采用化整为零的做法,从空间换时间,用一个数组cells,将一个value拆分进这个数组cells。多个线程需要同时对value进行操作时候,可以对线程id进行hash得到hash值,再根据hash值映射到这个数组cells的某个下标,再对该下标所对应的值进行自增操作。当所有线程操作完毕,将数组cells的所有值和无竞争值base都加起来作为最终结果(分散热点)


②. sum( )会将所有cell数组中的value和base累加作为返回值,核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中去,从而降级更新热点


③. 生活case,AtomicLong相当于是我们去超市买了一个牙刷,我们可以把它放到自己的口袋中,但是,如果我们需要在超市买很多东西,自己的口袋这个时候就装不下去了,我们可以使用LongAdder,它的一个核心思想是分散热点,base(相当于口袋)+cell数组(相当于袋子,数组中有两个元素,就相当于两个袋子装东西)


④. 内部是一个Base+一个Cell[ ]数组


base变量:非竞争状态条件下,直接累加到该变量上


Cell[ ]数组:竞争条件下(高并发下),累加各个线程自己的槽Cell[i]中


微信图片_20220108132718.png


④. 源码解析 longAdder.increment( )


①. add(1L)


  • ①. 最初无竞争时,直接通过casBase进行更新base的处理


  • ②. 如果更新base失败后,首次新建一个Cell[ ]数组(默认长度是2)


  • ③. 当多个线程竞争同一个Cell比较激烈时,可能就要对Cell[ ]扩容


  • ④. 源码如下:


微信图片_20220108132746.png


    LongAdder.java
  public void add(long x) {
    //as是striped64中的cells数组属性
    //b是striped64中的base属性
    //v是当前线程hash到的cell中存储的值
    //m是cells的长度减1,hash时作为掩码使用
    //a时当前线程hash到的cell
        Cell[] as; long b, v; int m; Cell a;
    /**
    首次首线程(as = cells) != null)一定是false,此时走casBase方法,以CAS的方式更新base值,
    且只有当cas失败时,才会走到if中
    条件1:cells不为空,说明出现过竞争,cell[]已创建
    条件2:cas操作base失败,说明其他线程先一步修改了base正在出现竞争
    */
        if ((as = cells) != null || !casBase(b = base, b + x)) {
      //true无竞争 fasle表示竞争激烈,多个线程hash到同一个cell,可能要扩容
            boolean uncontended = true;
      /*
      条件1:cells为空,说明正在出现竞争,上面是从条件2过来的,说明!casBase(b = base, b + x))=true
          会通过调用longAccumulate(x, null, uncontended)新建一个数组,默认长度是2
      条件2:默认会新建一个数组长度为2的数组,m = as.length - 1) < 0 应该不会出现,
      条件3:当前线程所在的cell为空,说明当前线程还没有更新过cell,应初始化一个cell。
          a = as[getProbe() & m]) == null,如果cell为空,进行一个初始化的处理
      条件4:更新当前线程所在的cell失败,说明现在竞争很激烈,多个线程hash到同一个Cell,应扩容
          (如果是cell中有一个线程操作,这个时候,通过a.cas(v = a.value, v + x)可以进行处理,返回的结果是true)
      **/
            if (as == null || (m = as.length - 1) < 0 ||
          //getProbe( )方法返回的时线程中的threadLocalRandomProbe字段
        //它是通过随机数生成的一个值,对于一个确定的线程这个值是固定的(除非刻意修改它)
                (a = as[getProbe() & m]) == null ||
                !(uncontended = a.cas(v = a.value, v + x)))
        //调用Striped64中的方法处理
                longAccumulate(x, null, uncontended);
        }


微信图片_20220108132810.png




相关文章
|
2天前
PandasTA 源码解析(二十三)
PandasTA 源码解析(二十三)
8 0
|
2天前
PandasTA 源码解析(二十二)(3)
PandasTA 源码解析(二十二)
5 0
|
2天前
PandasTA 源码解析(二十二)(2)
PandasTA 源码解析(二十二)
11 2
|
2天前
PandasTA 源码解析(二十二)(1)
PandasTA 源码解析(二十二)
8 0
|
2天前
PandasTA 源码解析(二十一)(4)
PandasTA 源码解析(二十一)
8 1
|
2天前
PandasTA 源码解析(二十一)(3)
PandasTA 源码解析(二十一)
8 0
|
2天前
PandasTA 源码解析(二十一)(2)
PandasTA 源码解析(二十一)
11 1
|
2天前
PandasTA 源码解析(二十一)(1)
PandasTA 源码解析(二十一)
9 2
|
2天前
PandasTA 源码解析(二十)(1)
PandasTA 源码解析(二十)
7 0
|
2天前
PandasTA 源码解析(十九)(3)
PandasTA 源码解析(十九)
10 2

推荐镜像

更多