LongAdder源码解析
AtomicLong类通过CAS提供了非阻塞的原子性操作,相比使用synchronized来说,性能已经有了很大的提升。但是,在高并发的场景下,大量线程同时使用AtomicLong时,只能有一个线程能成功,其他线程只能通过自旋获取新的值,再后续更新自己的值。这期间就浪费了大量的CPU资源。
JDK8新增了一个原子性递增或者递减类LongAdder用来克服在高并发下使用AtomicLong的缺点。
LongAdder类介绍
LongAdder内部维护了一个base变量和Cell数组。
- base变量是在非竞态条件下使用的,值直接累加到这个变量上
- Cell数组是在竞态条件下使用的,每个线程累加到不同的Cell上
最终获取值的时候,就通过累加base和每个Cell的值返回。
通过Base+Cell方式,LongAdder将原来AtomicLong的热点值从一个变成了多个,提高了并发度。但同时也增大了空间的损耗,典型的以空间换时间。
类图
LongAdder类继承了抽象了Striped64和实现而来接口Serializable。
1 | public class LongAdder extends Striped64 implements Serializable |
Striped64是实现高并发累加的工具类;Striped64的设计核心思路就是通过内部的分散计算来避免竞争。Striped64内部包含一个base和一个Cell[] cells数组,又叫hash表。
主要属性
主要方法
LongAdder()
1 | /** |
新建一个初始值为0的LongAdder。
add(long)
1 | /** |
add方法主要做的事情就是:
- 判断cells数组是否为null,如果为null的话,说明没有竞态条件,
- cas设置base的值,如果cas失败了,说明存在竞态条件,设置变量uncontended值为true
- 存在竞态的条件下,如果cells数组为null,调用longAccumulate方法,初始化cells数组
- 存在竞态的条件下,如果cells数组的长度为0,调用longAccumulate方法,初始化cells数组
- 如果cells数组不为null,并且长度不为0,那么计算出当前线程应该去哪个cell中累加,如果该位置的cell值为null,调用longAccumulate方法进行初始化
- 如果该位置上的值不为null,则cas累加,如果累加失败,调用longAccumulate方法,重新计算
increment()
1 | /** |
对LongAdder自增1,内部调用add方法。
decrement()
1 | /** |
对LongAdder自减1,内部调用add方法
sum
1 | /** |
返回base和所有cells数组值的累加和
reset
1 | /** |
将base值设置为0,如果cells数组有值,并且不为0,则重置为0.
sumThenReset()
1 | /** |
获取当前的和,并重置所有的数值为0.
toString()
1 | /** |
返回和的字符串形式
longValue()
1 | /** |
返回当前和
intValue()
1 | /** |
返回当前和的整型形式
floatValue()
1 | /** |
返回当前和的float形式
doubleValue()
1 | /** |
返回当前和的double形式