LongAdder源码

LongAdder和AtomicInteger的区别

LongAdder和AtomicInteger的作用就是在多线程并发的环境下保证数值计算正确。

LongAdder

优点:在多线程竞争激烈的情况下相比于AtomicInteger可减少cpu资源消耗
缺点:数值实时性不高,内存消耗较多

AtomicInteger

优点:比LongAdder消耗内存少,且数值实时性高
缺点:竞争激烈的情况下 只有一个线程能修改成功,太浪费cpu资源。

源码解析

用一个例子来说明LongAdder执行流程

public static void main(String[] args) throws InterruptedException {

		LongAdder longAdder = new LongAdder();
		for (int i = 0; i < 10; i++) {
			new Thread( () ->{
				longAdder.add(1); //10个线程累加1
			}).start();
		}
		Thread.sleep(1000);
		long sum = longAdder.sum();//计算总和
		System.out.println(sum);

	}

主要方法就是add() 和 sum()

先说明几个变量

	/**
     * Table of cells. When non-null, size is a power of 2.
     * 单元格表。非空时,大小为2的幂。
     */
    transient volatile Cell[] cells;

    /**
     * Base value, used mainly when there is no contention, but also as
     * a fallback during table initialization races. Updated via CAS.
     * 基本值,主要在没有争用时使用,但也用作表初始化竞争期间的回退。通过CAS更新
     */
    transient volatile long base;

     
	/**
	锁,值为0时说明被其他线程占用
	*/
	transient volatile long cellsBusy;

SUM() 解析

sum方法是用于计算cell数组中的数值总和,很明显能看到,通过for循环来对cell中的数值进行累加后返回。

public long sum() {
    Striped64.Cell[] var1 = this.cells;
    long var3 = this.base;// 没有多线程竞争时,使用此变量进行累加。
    if (var1 != null) {
        for(int var5 = 0; var5 < var1.length; ++var5) {
            Striped64.Cell var2;
            if ((var2 = var1[var5]) != null) {
                var3 += var2.value;
            }
        }
    }

    return var3;
}

在多线程场景下,有可能数组下标0,1,2这三个位置的数值已经累加完成,此时正在累加数组下标为4的数值,那么0,1,2 这三个位置的数值如果再有其他线程去修改,那么在本次执行的sum() 中,是不会进行累加的。这就解释了为什么sum数值实时性不高。

add() 方法解析

public void add(long var1) {
    Striped64.Cell[] var3;
    long var4;
    if ((var3 = this.cells) != null  //判断cells数组是否为空,为空说明是未初始化或者不是多线程场景
    || !this.casBase(var4 = this.base, var4 + var1)) // casBase方法内部通过cas进行修改值,修改成功说明没有其他线程竞争
    {
        boolean var10 = true;
        long var6;
        int var8;
        Striped64.Cell var9;
        if (var3 == null  //说明未初始化cells数组
        || (var8 = var3.length - 1) < 0 //说明未初始化cells数组
        || (var9 = var3[getProbe() & var8]) == null // 通过getProbe() & var8计算当前线程的数组下标
        || !(var10 = var9.cas(var6 = var9.value, var6 + var1))) { // 再次cas修改值
            this.longAccumulate(var1, (LongBinaryOperator)null, var10); // 上面条件失败后进入核心方法,初始化cells数组或者扩容等
        }
    }

}


final boolean casBase(long var1, long var3) {
    return UNSAFE.compareAndSwapLong(this, BASE, var1, var3);
}

static final int getProbe() {
    return UNSAFE.getInt(Thread.currentThread(), PROBE);
}
	/**
     * Handles cases of updates involving initialization, resizing,
     * creating new Cells, and/or contention. See above for
     * explanation. This method suffers the usual non-modularity
     * problems of optimistic retry code, relying on rechecked sets of
     * reads.
     *
     * @param x the value
     * @param fn the update function, or null for add (this convention
     * avoids the need for an extra field or function in LongAdder).
     * @param wasUncontended false if CAS failed before call
     */
	/**
	处理涉及初始化、调整大小、创建新单元格和或争用的更新情况。请参阅上面的说明。
	这种方法遇到了乐观重试代码的常见非模块化问题,依赖于重新检查的读取集
	@param x更新函数的值
	@param fn,或add为null(此约定避免了在LongAdder中需要额外的字段或函数)
	@param wasUnconsent 如果CAS在调用前失败,则为false
	*/

final void longAccumulate(long x, LongBinaryOperator fn,
                          boolean wasUncontended) {
    // 用于通过 h & cells.length -1 计算线程操作的位置
    int h;
    // 判断线程的probe是否初始化
    if ((h = getProbe()) == 0) {
        // 初始化线程probe
        ThreadLocalRandom.current(); // force initialization
        h = getProbe();
        wasUncontended = true;
    }
    // 用于记录cell数组当前是否存在空的桶,false表示大概率存在空桶,及时满足拓容条件也暂时先不拓容
    boolean collide = false;                // True if last slot nonempty 如果最后一个插槽为非空,则为True
    for (;;) {
        Cell[] as; Cell a; int n; long v;
        // cells 是否为空
        if ((as = cells) != null && (n = as.length) > 0) {
            if ((a = as[(n - 1) & h]) == null) {
                // cells 不为空,并且线程操作桶为null,则new Cell(x)并更新
                if (cellsBusy == 0) {       // Try to attach new Cell
                    Cell r = new Cell(x);   // Optimistically create
                    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;
            }
            // 在LongAdder.add()方法中已经cas过一次,如果probe没有更新,则直接进行a.cas操作大概率失败,则加了此次判断
            else if (!wasUncontended)       // CAS already known to fail
                wasUncontended = true;      // Continue after rehash
            else if (a.cas(v = a.value, ((fn == null) ? v + x :
                                         fn.applyAsLong(v, x))))
                break;
            // cell数组长度大于cpu数,则后续不再拓容
            // cells != as 正在拓容,则下次循环有空桶的概率较大 将collide = false,下次执行到此处则会advanceProbe(h) 一次而非直接拓容
            else if (n >= NCPU || cells != as)
                collide = false;            // At max size or stale
            else if (!collide)
                collide = true;
            // 拓容,每次为 数组长度 << 1
            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
            }
            // 每次失败后都会修改probe值,重新进入循环,而非probe不变
            h = advanceProbe(h);
        }
        // cells 为null,则初始化cells,初始size为2 ,casCellsBusy是使用cas加锁,表明正在扩容
        else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
            boolean init = false;
            try {                           // Initialize table 初始化表
                if (cells == as) {
                    Cell[] rs = new Cell[2];
                    rs[h & 1] = new Cell(x);
                    cells = rs;
                    init = true;
                }
            } finally {
                cellsBusy = 0;
            }
            if (init)
                break;
        }
        // cells 为null ,并且cellsBusy 锁竞争失败,则其他线程正在初始化,尝试casBase
        else if (casBase(v = base, ((fn == null) ? v + x :
                                    fn.applyAsLong(v, x))))
            break;                          // Fall back on using base
    }
}

总结

LongAdder在单线程的时候通过base变量存储值,而多线程竞争情况下,使用数组分摊多次计算,让多个线程通过计算探针值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值