type
status
date
slug
summary
tags
category
icon
password
原文
JDK 8 引入的一个高并发计数器类。LongAdder 是 AtomicLong 的高并发优化版本。
核心是将冲突分散不同的槽,减少CAS失败重试次数造成的CPU空转、自旋浪费。
适合读多写少、允许最终一致的统计类操作。
适合场景:
- 限流、监控 如 Sentinel统计时间窗口内请求量
- 指标监控、缓存命中次数等
常用方法
void increment()自增
void add(long x)添加指定计数
void decrement()自减
increment()、decrement() 都是调用 add方法。increment() → add(1L)decrement() → add(-1L)long sum()返回当前总和。这是一个近似值,不保证绝对准确
LongAdder 的构造器没有任何参数,只支持从 0 递增或递减。
重要属性
- Cell[] cells
高并发下分散线程操作,减少竞争
- transient volatile long base;
用于无竞争的情况下直接更新,或者作用备用更新机制。
- cellsBusy 锁的标志位。
计数操作
计数包括递增、递减、增加指定计数。这底层都是调用 add 方法。
accumulate
LongAdder 内部的cells数组和base两个属性来维护计数器的值。
base 是基础数值,用于低竞争的时更新。在高并发的情况下,更新的失败几率非常大,则去 cells数组中去更新。
longAccumulate
longAccumulate方法的作用
- 初始化 cell 数组
- 当前线程创建 cell对象加入到 cell 数组中。
probe是存储在线程对象 Thread 里的一个整数字段。每个线程都有自己的 probe 值,这个值在每个线程内部是独立的。
在 LongAdder中probe 值是用来计算数组索引的。
当前 Cell 数组已经初始化的情况下,判断当前线程对应的槽位是否为 null。
数组初始化说明已经有线程开始竞争了。
对应的槽位为 null,说明当前的槽位还没有线程操作,此时如果是无锁的情况下且拿到锁的情况下,创建 Cell 对象并加入到 cells 数组的槽中。
更新数组元素时必须拿到锁标志,因为高并发情况下不加锁会导致数组元素覆盖成不正确的情况。
上述步骤成功以后退出循环,方法结束。
根据当前线程的 probe 的计算的数组位置已有 cell元素,直接更新元素值。如果CAS 成功表示计数成功了,直接break 出循环,方法结束。
在该步骤之前 有个collide变量 ,该变量是一个碰撞检测标志,用于跟踪线程在尝试更新 Cell 数组的时候有无发生冲突。
collide = false 表示没有发生碰撞
collide = true 线程发现目标 Cell 位置被占用且 CAS 更新失败时。
执行到该处说明并发情况已经非常激烈,尝试扩容 cells 数组来进一步分散热点。
初始化数组。首先执行计数操作且第一个拿到锁的线程会在这里进行初始化cells 数组操作。因为执行accumulate方法中的 if 条件是判断 cells ≠ null。LongAdder 在构造器中并没有初始化该数组,所以首先执行的线程肯定会来这里先初始化数组。
再次尝试 CAS 更新 base值,可以算是一个兜底或者说降级方案。这里是在 cells数组还没初始化,准备去抢占锁去初始化发现锁已经被别的线程拿到了,cas去更新base值,减少自旋时间的一种尝试。
数组索引计算
probe:当前线程的 probe 值,这是一个用于哈希计算的整数值。
n : cells数组的长度,长度保持在 2 的幂次方。
index = (n-1) & probe 等价于 (n-1) % probe
这样能保证索引在[0,n-1]的区间且均匀分布。cells的数组长度也是掩码.
同 HashMap、ConcurrentHashMap等类计算数组索引一样。
获取计数
获取计数只能获取快照或者说是近似值。
LongAdder 读写并不互斥。如果写线程都已计数操作都已完成,那么再去读计数是最终一致的。
总的计数= base + cells数组中的每个元素的值。
- Author:newrain-zh
- URL:https://alex.sh.cn/article/2aa332f2-d485-8051-853c-ed6225db375b
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!


