并发包源码解读——AtomicLong&LongAdder&LongAccumulator

并发包源码解读——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);
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wheat_Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值