DoubleAdder源码解析

DoubleAdder源码解析

DoubleAdder类介绍

AtomicLong类通过CAS提供了非阻塞的原子性操作,相比使用synchronized来说,性能已经有了很大的提升。但是,在高并发的场景下,大量线程同时使用AtomicLong时,只能有一个线程能成功,其他线程只能通过自旋获取新的值,再后续更新自己的值。这期间就浪费了大量的CPU资源。

JDK8新增了一个原子性递增或者递减类DoubleAdder用来克服在高并发下使用AtomicLong的缺点。

类图

DoubleAdder类图

主要属性

主要方法

DoubleAdder()

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

创建一个新的DoubleAdder对象并初始化为0

add(double)

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

如果cells数组不为空,并且cas(base,base+x)失败,设置竞态条件为true。

如果cells数组为null,或者长度为0,又或者当前线程对应的cell数组位置上cell值为null。再或者cas(base,base+x)再失败,就调用父类的striped64的doubleAccumulate方法。

sum()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 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. Also, because floating-point arithmetic is not
* strictly associative, the returned result need not be identical
* to the value that would be obtained in a sequential series of
* updates to a single variable.
*
* @return the sum
*/
public double sum() {
Cell[] as = cells; Cell a;
double sum = Double.longBitsToDouble(base);
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
sum += Double.longBitsToDouble(a.value);
}
}
return sum;
}

返回base和当前所有cell上的值的和

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; // relies on fact that double 0 must have same rep as long
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
a.value = 0L;
}
}
}

base设置为0,然后每个非空的cell上的值设置为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
25
/**
* 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 double sumThenReset() {
Cell[] as = cells; Cell a;
double sum = Double.longBitsToDouble(base);
base = 0L;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null) {
long v = a.value;
a.value = 0L;
sum += Double.longBitsToDouble(v);
}
}
}
return sum;
}

返回当前的和,并重置

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 Double.toString(sum());
}

返回当前和的字符串形式

doubleValue()

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

返回当前和

longValue()

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

返回当前和并转为long型。注意参数溢出,这是一个向下转型。

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();
}

返回当前和并转为int型。注意参数溢出,这是一个向下转型。

floatValue()

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

返回当前和并转为float型,注意参数溢出,这是一个向下转型。