详细分析LongAdder原理

前言

适合阅读者:看了源码但没看懂,看得头大的
本文参考源码:JDK8版本
本文作用:
1.深入理解LongAdder原理
2.辅助源码阅读

阅读建议:配合JDK8源码阅读

总体过程就是这样

截图

详细分析

1.类的结构是怎样的

继承了Striped64抽象类,这个抽象类中有cells数组,base变量,有longAccumulate方法

LongAdder里自己定义了add方法和sum方法.


2.调用add(x)方法时,发生了什么

第一步.先判断cells有没有被初始化

1.没有过初始化------代表此时没有竞争发生过,CAS累加base变量

1.1 累加成功-----add方法执行完毕

1.2 累加失败----此时进入第二步

2.cells有过初始化-----代表此时发生过竞争,进入第二步

第二步.当1.cells数组有过初始化, 2.当前线程需要操作的cells数组中的那个位置已经创建了一个cells对象 3.CAS累加数据成功,则弹出add方法


这三个条件有任意一个未满足,调用longAccumulate方法


3.调用longAccumulate方法,发生了什么

调用这个方法的原因—解决上述三个情况任意一种

根据进入longAccumulate方法的三种情况,可以知道longAccumulate就是为了解决这三个情况下如何实现累加操作而诞生的

1.处理 Cells 数组未初始化的情况

  1. 检查cellsBusy(锁标记),如果为0(表示无锁),尝试加锁(置1),加锁成功进入下一步
  2. 初始化长度为2的cells数组
  3. 根据add(x)时传入的x,创建一个值为x的cell对象,根据当前线程的hash值&1得出存放到cells数组的位置,放入
  4. 释放cellBusy(置0).
  5. 结束方法

如果获取cellsBusy失败的话-----失败回退:如果其他线程正在初始化(cellsBusy 被占用),则尝试直接通过 CAS 累加到 base 变量(兜底方案)。


2.处理当前线程在cells数组中需要操作的位置未被占用(该位置为 null)的情况

  1. cellsBusy是否为0,为0进入下一步
  2. 创建新 Cell:生成一个值为 x 的新 Cell 对象。
  3. 再次检查 cellsBusy 锁,确保无竞争后,CAS 加锁。
  4. 二次检查:加锁后再次确认该位置仍为 null(避免其他线程已修改)。
  5. 插入 Cell:将新 Cell 放入数组对应位置,释放锁(置0)。
  6. 结束方法.

如果获取cellBusy锁失败-----失败重试:如果插入过程中被其他线程抢占(如该位置已被填充),则回到循环重试。

3.处理 当前线程在cells数组中需要操作的位置存在竞争(CAS 操作失败)的情况

1.把wasUncontended设置为true,为的是调用一次advanceProbe(h)重新生成当前线程hash值,换一下当前线程需要操作的位置

2.尝试两次CAS**(每一次都重新生成当前线程hash值,使用新的所要累加的位置,)**,累加当前位置上的值(当前值加x),

2.1 如果有一次CAS成功,结束方法(退出这个情况的唯一可能)

2.2 如果两次都失败,进入判断是否进入需要扩容,需要就扩容.不需要就不断重试

3.数组是否需要扩容

3.1 当数组长度小于CPU核数,扩容:通过 CAS 获取 cellsBusy 锁(置1),将数组大小翻倍,复制旧数据到新数组,释放锁(置0)。重新执行操作2.

3.2 当数组长度大于等于CPU核数,不再扩容,调用advanceProbe(h)重新生成当前线程需要累加的位置,不断重试操作2,直到成功


我写的这个步骤其实有错误,这些步骤里的返回到操作2是错误的,实际上返回到的是第二个情况(判断当前线程在cells数组中需要操作的位置未被占用(该位置为 null))那里,这里为了理解起来方便些,描述为返回到操作2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值