LongAdder 是在 JDK 1.8 的时候新增了一个原子类,如果是 count++ 操作,推荐使用 AtomicInteger 类;如果是 JDK 1.8 或之后版本,一般情况下,在高竞争的情况下比 AtomicInteger 效率要高,主要是因为内部有一个成员变量 cells 。
AtomicInteger 只有一个 value,在高并发情况下,线程争用非常严重,而 LongAdder 有两个值可以用来累加,一个是 base ,它的作用类似于 AtomicInteger 中的 value,在没有并发的情况下进行累加,而 cells 为 null,cells 只会在有竞争的时候进行初始化,默认数组长度为 2 ,每次扩容都会变成原来的两倍。
成员变量cells
在没有并发的情况下,用不到 cells,有竞争之后,会初始化 cells,第一次长度为 2 ,每次扩容 2 倍,到数组长度大等于 CPU 线程数就不再扩容。每个线程会对 cells 计算过位置的 Cell 对象的 value 进行累加。
成员变量 cellsBusy
cellsBusy,它有两个值0 或1,它的作用是当要修改cells数组时加锁,防止多线程同时修改cells数组,0为无锁,1为加锁,加锁的状况有三种
- cells数组初始化的时候;
- cells数组扩容的时候;
- 如果cells数组中某个元素为null,给这个位置创建新的Cell对象的时候;
成员变量base
它有两个作用:
- 在开始没有竞争的情况下,将累加值累加到base
- 在 cells 初始化时,cells 不可用,这时会尝试将值累加到 base上;
伪共享问题,JDK 1.8 使用 @Contended 注解解决
原文地址:blog.csdn.net/zqz_zqz/art…
(blog.csdn.net/zqz_zqz/art…)
LondAdder 进行 increment() 的时候,采用的是分段式 CAS 锁的方式,且只锁 cells 上属于自己的那个位置的数,减少了乐观锁的次数,所以在高竞争的情况下有很大的优势,但是如果本身的执行机制大于 CAS 自旋的时间,效率就不够好了。
而取值的 intvalue() 方法,就是用 base + cells 上所有的数。