LongAdder源码解析

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类图

主要属性

主要方法

LongAdder()

1
2
3
4
5
6
/**
* Creates a new adder with initial sum of zero.
*/
public LongAdder() {
}

新建一个初始值为0的LongAdder。

add(long)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Adds the given value.
*
* @param x the value to add
*/
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
if ((as = cells) != null || !casBase(b = base, b + x)) {
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
longAccumulate(x, null, uncontended);
}
}

add方法主要做的事情就是:

  1. 判断cells数组是否为null,如果为null的话,说明没有竞态条件,
  2. cas设置base的值,如果cas失败了,说明存在竞态条件,设置变量uncontended值为true
  3. 存在竞态的条件下,如果cells数组为null,调用longAccumulate方法,初始化cells数组
  4. 存在竞态的条件下,如果cells数组的长度为0,调用longAccumulate方法,初始化cells数组
  5. 如果cells数组不为null,并且长度不为0,那么计算出当前线程应该去哪个cell中累加,如果该位置的cell值为null,调用longAccumulate方法进行初始化
  6. 如果该位置上的值不为null,则cas累加,如果累加失败,调用longAccumulate方法,重新计算

increment()

1
2
3
4
5
6
/**
* Equivalent to {@code add(1)}.
*/
public void increment() {
add(1L);
}

对LongAdder自增1,内部调用add方法。

decrement()

1
2
3
4
5
6
/**
* Equivalent to {@code add(-1)}.
*/
public void decrement() {
add(-1L);
}

对LongAdder自减1,内部调用add方法

sum

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Returns the current sum. The returned value is <em>NOT</em> an
* atomic snapshot; invocation in the absence of concurrent
* updates returns an accurate result, but concurrent updates that
* occur while the sum is being calculated might not be
* incorporated.
*
* @return the sum
*/
public long sum() {
Cell[] as = cells; Cell a;
long sum = base;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
sum += a.value;
}
}
return sum;
}

返回base和所有cells数组值的累加和

reset

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Resets variables maintaining the sum to zero. This method may
* be a useful alternative to creating a new adder, but is only
* effective if there are no concurrent updates. Because this
* method is intrinsically racy, it should only be used when it is
* known that no threads are concurrently updating.
*/
public void reset() {
Cell[] as = cells; Cell a;
base = 0L;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
a.value = 0L;
}
}
}

将base值设置为0,如果cells数组有值,并且不为0,则重置为0.

sumThenReset()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Equivalent in effect to {@link #sum} followed by {@link
* #reset}. This method may apply for example during quiescent
* points between multithreaded computations. If there are
* updates concurrent with this method, the returned value is
* <em>not</em> guaranteed to be the final value occurring before
* the reset.
*
* @return the sum
*/
public long sumThenReset() {
Cell[] as = cells; Cell a;
long sum = base;
base = 0L;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null) {
sum += a.value;
a.value = 0L;
}
}
}
return sum;
}

获取当前的和,并重置所有的数值为0.

toString()

1
2
3
4
5
6
7
/**
* Returns the String representation of the {@link #sum}.
* @return the String representation of the {@link #sum}
*/
public String toString() {
return Long.toString(sum());
}

返回和的字符串形式

longValue()

1
2
3
4
5
6
7
8
/**
* Equivalent to {@link #sum}.
*
* @return the sum
*/
public long longValue() {
return sum();
}

返回当前和

intValue()

1
2
3
4
5
6
7
/**
* Returns the {@link #sum} as an {@code int} after a narrowing
* primitive conversion.
*/
public int intValue() {
return (int)sum();
}

返回当前和的整型形式

floatValue()

1
2
3
4
5
6
7
/**
* Returns the {@link #sum} as a {@code float}
* after a widening primitive conversion.
*/
public float floatValue() {
return (float)sum();
}

返回当前和的float形式

doubleValue()

1
2
3
4
5
6
7
/**
* Returns the {@link #sum} as a {@code double} after a widening
* primitive conversion.
*/
public double doubleValue() {
return (double)sum();
}

返回当前和的double形式

参考资料

LongAdder源码深入剖析

0%