Jdk1.8 JUC源码增量解析(2)-atomic-LongAdder和LongAccumulator
作者:大飞
功能简介:
- LongAdder是jdk1.8提供的累加器,基于Striped64实现。它常用于状态采集、统计等场景。AtomicLong也可以用于这种场景,但在线程竞争激烈的情况下,LongAdder要比AtomicLong拥有更高的吞吐量,但会耗费更多的内存空间。
- LongAccumulator和LongAdder类似,也基于Striped64实现。但要比LongAdder更加灵活(要传入一个函数接口),LongAdder相当于是LongAccumulator的一种特例。
源码分析:
- 先看一下LongAdder类,看下结构:
public class LongAdder extends Striped64 implements Serializable {
private static final long serialVersionUID = 7249069246863182397L;
/**
* Creates a new adder with initial sum of zero.
*/
public LongAdder() {
}
LongAdder继承了Striped64,本身没有任何域。
- 再看一下LongAdder的方法:
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
//如果cell表为null,会尝试将x累加到base上。
if ((as = cells) != null || !casBase(b = base, b + x)) {
/*
* 如果cell表不为null或者尝试将x累加到base上失败,执行以下操作。
* 如果cell表不为null且通过当前线程的probe值定位到的cell表中的Cell不为null。
* 那么尝试累加x到对应的Cell上。
*/
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
//或者cell表为null,或者定位到的cell为null,或者尝试失败,都会调用下面的Striped64中定义的longAccumulate方法。
longAccumulate(x, null, uncontended);
}
}
add方法逻辑很简单,先尝试将x累加到base上,失败的话再看看能不能从cell表中找到cell,找到的话再尝试将x累加到这个cell里面,还失败的话就调用longAccumulate方法,这个方法上篇分析Striped64的时候分析过。
/**
* Equivalent to {@code add(1)}.
*/
public void increment() {
add(1L);
}
/**
* Equivalent to {@code add(-1)}.
*/
public void decrement() {
add(-1L);
}
递增和递减方法,不需要解释了。
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;
}
sum方法就是获取当前LongAdder值的总和,包括base和cells value两部分。
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和cells value两部分值都置为0。
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;
}
获取总和后重置。
LongAdder间接继承了Number,看下相关的方法实现:
public long longValue() {
return sum();
}
public int intValue() {
return (int)sum();
}
public float floatValue() {
return (float)sum();
}
public double doubleValue() {
return (double)sum();
}
LongAdder的序列化使用序列化代理模式:
private static class SerializationProxy implements Serializable {
private static final long serialVersionUID = 7249069246863182397L;
private final long value;
SerializationProxy(LongAdder a) {
value = a.sum();
}
private Object readResolve() {
LongAdder a = new LongAdder();
a.base = value;
return a;
}
}
private Object writeReplace() {
return new SerializationProxy(this);
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.InvalidObjectException {
throw new java.io.InvalidObjectException("Proxy required");
}
- 再看一下LongAccumulator类,先看结构:
public class LongAccumulator extends Striped64 implements Serializable {
private static final long serialVersionUID = 7249069246863182397L;
private final LongBinaryOperator function;
private final long identity;
public LongAccumulator(LongBinaryOperator accumulatorFunction,
long identity) {
this.function = accumulatorFunction;
base = this.identity = identity;
}
LongAccumulator和LongAdder不同,内部有一个函数接口和一个初始值。
- 再看LongAccumulator的方法:
public void accumulate(long x) {
Cell[] as; long b, v, r; int m; Cell a;
if ((as = cells) != null ||
(r = function.applyAsLong(b = base, x)) != b && !casBase(b, r)) {
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended =
(r = function.applyAsLong(v = a.value, x)) == v ||
a.cas(v, r)))
longAccumulate(x, function, uncontended);
}
}
和LongAdder的add方法一样的逻辑。
public long get() {
Cell[] as = cells; Cell a;
long result = base;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
result = function.applyAsLong(result, a.value);
}
}
return result;
}
将内部所有的零散值通过函数算出一个最终值。
public void reset() {
Cell[] as = cells; Cell a;
base = identity;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
a.value = identity;
}
}
}
public long getThenReset() {
Cell[] as = cells; Cell a;
long result = base;
base = identity;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null) {
long v = a.value;
a.value = identity;
result = function.applyAsLong(result, v);
}
}
}
return result;
}
注意这里和LongAdder不同,这里的重置会将base和cells value都重置成初始值-identity。 其他的Number方法和序列化方式和LongAdder一样。
代码解析完毕!
参见:Jdk1.8 JUC源码增量解析(1)-atomic-Striped64
参见:Jdk1.6 JUC源码解析(1)-atomic-AtomicXXX