一、类签名
利用一个或多个变量共同维护一个初始值为 0 的 long 总和。当调用 add() 时出现线程竞争,这些变量集分别动态增加,减少对同一个锁的竞争。
方法 sum() 或变量 longValue,返回当前用于维护总和变量集的总大小。
1
public class LongAdder extends Striped64 implements Serializable
在多线程下更新总和值,例如进行统计数据的收集,而不是用于细粒度的同步控制,相比 AtomicLong,此类是更好的选择。
在较少竞争的情况下,此类和 AtomicLong 特性基本相似。当在高竞争的情况下,本类的吞吐量明显更高,同时也消耗更多内存空间。
LongAdders 能和 ConcurrentHashMap 一起使用去维护一个频繁伸缩的 map。例如,添加计数值到ConcurrentHashMap<String, LongAdder> freqs
,键不存在时进行初始化,可通过freqs.computeIfAbsent(key, k -> new LongAdder()).increment();
实现。
此类继承自 Number,但没有定义如 equals、hashCode、compareTo 等方法,因为实例会发生变化,所以不能用作集合的键。源码版本 JDK11。
二、构造方法
默认构造方法,类完成构造后初始总和为 0。
1
2
public LongAdder() {
}
三、成员方法
此方法能把指定参数值 x 增加到目标值上。如果该参数传递负数,则意味着从总数上减去指定值。
1
2
3
4
5
6
7
8
9
10
11
public void add(long x) {
Cell[] cs; long b, v; int m; Cell c;
if ((cs = cells) != null || !casBase(b = base, b + x)) {
boolean uncontended = true;
if (cs == null || (m = cs.length - 1) < 0 ||
(c = cs[getProbe() & m]) == null ||
!(uncontended = c.cas(v = c.value, v + x)))
// 调用父类的Striped64()
longAccumulate(x, null, uncontended);
}
}
以下两个方法通过调用 add() 方法实现数值递增和递减。递增传递的参数是 1L,递减传递的参数是 -1L。
1
2
3
4
5
6
7
public void increment() {
add(1L);
}
public void decrement() {
add(-1L);
}
返回当前的总计数值。返回的值不是原子性的快照,就是一个基本类型的 long。在没有并发更新的情况下调用会返回准确结果。但是在计算总和时发生的并发更新,可能不会被算到总和内。
1
2
3
4
5
6
7
8
9
10
11
12
13
public long sum() {
// 从父类获取cells
Cell[] cs = cells;
long sum = base;
if (cs != null) {
for (Cell c : cs)
if (c != null)
// 从cell获取记录值并累加到sum
sum += c.value;
}
// 返回sum的值
return sum;
}
重置维护总计值的变量值到 0。对比创建一个新的实例,通过此方法重置总计值后复用实例,是个更好的选择,但也仅在更新的时候,没有其他线程并发添加值。
因为这个方法很活跃,只能在确认没有并发更新的时候使用。
1
2
3
4
5
6
7
8
9
10
11
public void reset() {
// 从父类获取cells
Cell[] cs = cells;
base = 0L;
if (cs != null) {
// 逐个重置cell保存的值
for (Cell c : cs)
if (c != null)
c.reset();
}
}
方法获取总和后重置原始值为 0,并把保存的值作为结果返回,相当于 sum() 和 reset() 的合并调用。
如果调用此方法的同时,还有其他线程在进行更新操作,此方法的 返回值 和 重置前存储的值 不保证是一致的。因为先返回值,值在其他线程继续修改,最后才被重置为 0。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public long sumThenReset() {
// 从父类获取cells
Cell[] cs = cells;
// 获取现在的总计值
long sum = getAndSetBase(0L);
if (cs != null) {
for (Cell c : cs) {
if (c != null)
// 获取总值并重置cell
sum += c.getAndSet(0L);
}
}
// 返回sum的值
return sum;
}