并发包源码解读——AtomicLong&LongAdder&LongAccumulator
之前写了StampedLock源码简直要吐血,发现看的太细了好像也不太好,今天看点容易看懂的
由于为了跟LongAdder比较,特意选了AtomicLong,其他的还有AtomicInteger、AtomicBoolean等等
1、AtomicLong
以一个简单的例子起步
public static void main(String[] args) {
final AtomicLong atomicLong = new AtomicLong();
atomicLong.getAndIncrement();
}
1.1、getAndIncrement
返回当前值然后自增1,他是getAndAddLong的封装
很简单的逻辑,自旋调用getLongVolatile取值,然后通过cas更新
public final long getAndIncrement() {
return unsafe.getAndAddLong(this, valueOffset, 1L);
}
public final long getAndAddLong(Object var1, long var2, long var4) {
long var6;
do {
var6 = this.getLongVolatile(var1, var2);
} while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
return var6;
}
1.2、incrementAndGet
自增1然后返回自增后的值,只不过比getAndIncrement多加了1
public final long incrementAndGet() {
return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}
其他的方法也都差不多,没什么可说的
2、LongAdder
先说特点,LongAdder比AtomicLong吞吐量高,但是在读写并发执行的时候不能保证可靠性
然后再介绍一下LongAdder,它采用了负载,把自己的值拆分成多个值,每个值都有自己的锁,这样多个线程对LongAdder操作时就会把锁加到不同的值上,降低了锁的竞争
下面我们以一个最简单的例子开始
public static void main(String[] args) {
final LongAdder longAdder = new LongAdder();
longAdder.increment();
}
increment这个方法是自增1,它是add方法的封装,并且没有返回值(竟然没有返回值,可见返回值并不可靠)
public void increment() {
add(1L);
}
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
//这个base看样子就是实际的值,首先cas加一下试试
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);
}
}
unContended为false说明cas修改单个cell的值失败了,调longAccumulate,这个方法是父类Striped64的方法
final void longAccumulate(long x, LongBinaryOperator fn,
boolean wasUncontended) {
int h;
//取了ThreadLocalRandom里的probe属性,如果是0就初始化ThreadLocalRandom,初始化后的probe为1
if ((h = getProbe()) == 0) {
ThreadLocalRandom.current(); // force initialization
h = getProbe();
wasUncontended = true;
}
boolean collide = false; // True if last slot nonempty
//看来本质也是一个自旋
for (;;) {
//cell就是用来存每个被分解的值
Cell[] as; Cell a; int n; long v;
//说明cell已经初始化过了
if ((as = cells) != null && (n = as.length) > 0) {
//cells数组中最后一个值对象为空,说明可以在数组最后一个位置初始化值对象
if ((a = as[(n - 1) & h]) == null) {
//cellsBusy为1表示有值对象正在被创建,0反之
if (cellsBusy == 0) { // Try to attach new Cell
Cell r = new Cell(x); // Optimistically create
//cas成功了就说明获取到锁了,可以创建值对象
if (cellsBusy == 0 && casCellsBusy()) {
boolean created = false;
try { // Recheck under lock
Cell[] rs; int m, j;
if ((rs = cells) != null &&
(m = rs.length) > 0 &&
rs[j = (m - 1) & h] == null) {
rs[j] = r;
created = true;
}
} finally {
cellsBusy = 0;
}
if (created)
break;
continue; // Slot is now non-empty
}
}
collide = false;
}
//说明数组满了
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
//说明数组满了并且没有竞争,就cas改其中一个值对象里的值
else if (a.cas(v = a.value, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break;
//说明cas失败了,同时改了同一个值对象。线程数到达cpu上限或者改cells时,这次就不竞争了
else if (n >= NCPU || cells != as)
collide = false; // At max size or stale
//这个线程不竞争了,下次肯定要竞争
else if (!collide)
collide = true;
//值对象太少导致负载高,继续创建值对象均摊负载
else if (cellsBusy == 0 && casCellsBusy()) {
try {
if (cells == as) { // Expand table unless stale
Cell[] rs = new Cell[n << 1];
for (int i = 0; i < n; ++i)
rs[i] = as[i];
cells = rs;
}
} finally {
cellsBusy = 0;
}
collide = false;
continue; // Retry with expanded table
}
//最后都要改变随机数
h = advanceProbe(h);
}
//说明cells还没初始化,如果没有线程正在创建cell,cells也没变,就cas改cellsBusy,表示此刻要创建cell
else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
boolean init = false;
try { // Initialize table
//如果没有别的线程创建cells,就初始化一个大小为2的cells
if (cells == as) {
Cell[] rs = new Cell[2];
rs[h & 1] = new Cell(x);
cells = rs;
init = true;
}
} finally {
cellsBusy = 0;
}
if (init)
break;
}
else if (casBase(v = base, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break; // Fall back on using base
}
}
总结一句话就是,LongAdder会把一个总值拆分成多个子值,这样多个线程同时修改LongAdder就会分别对不同子值加锁,减少阻塞
3、LongAccumulator
LongAccumulator是LongAdder的功能加强版,可以自定义原子类的计算逻辑,不仅限于加
还是以最简单的例子开始,代码不规范的地方请忽视哈哈
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
LongAccumulator accumulator = new LongAccumulator(Long::sum,0);
for (int i = 1;i<=100;i++){
int finalI = i;
executorService.submit(() -> accumulator.accumulate(finalI));
}
executorService.shutdown();
while (!executorService.isTerminated()){
}
System.out.println(accumulator.longValue());
}
3.1、构造器
参数传入二元函数和初始值
public LongAccumulator(LongBinaryOperator accumulatorFunction,
long identity) {
this.function = accumulatorFunction;
base = this.identity = identity;
}
3.2、accumulate
与LongAdder的add实现原理基本一致,不再赘述
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);
}
}