手撕LongAdder-add()源码

最近喜欢上阅读国外大牛写的源码,真的感觉每一步都是精华,膜拜膜拜。
特此将代码的每一步解析总结出来分享给大家~~~

/**
     * Adds the given value.
     *
     * @param x the value to add
     */
    public void add(long x) {
        //as: 表示cells引用
        //b: 表示获取的base值
        //v: 表示 期望值
        //m: 表示cells数组长度
        //a: 表示当前线程命中的cell单元格
        Cell[] as; long b, v; int m; Cell a;
        //条件一:true -> cells数组已经初始化了,当前线程需要将数据写入到对应的cell中
        //       false -> 表示cells未初始化,当前所有线程应该将数据写到base中
        //条件二:true -> 表示发生竞争了,可能需要 重试 或者 扩容
        //       false -> 表示当前线程cas替换数据成功
        if ((as = cells) != null || !casBase(b = base, b + x)) {
            //进入的条件:
            //1.cells数组已经初始化了,当前线程需要将数据写入到对应的cell中
            //2.表示发生竞争了,可能需要 重试 或者 扩容

            //是否有竞争:true -> 没有竞争
            //           false -> 有竞争
            boolean uncontended = true;
            //条件一:true -> cells 未初始化,即多线程写base发生了竞争
            //       false -> cells 已经初始化,继续执行 条件二
            //条件二:getProbe() 获取当前线程的hash值 m表示cells数组长度 - 1 cells数组长度一定是2的次方数 15 = b1111
            //       true -> 说明当前线程对应下标的cell为空,继续执行 longAccumulate()方法
            //       false -> 说明当前线程对应下标的cell不为空,继续执行 条件三
            //条件三:true -> 说明cas失败,意味着当前线程对应的cell有竞争
            //       false -> 说明cas成功
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[getProbe() & m]) == null ||
                !(uncontended = a.cas(v = a.value, v + x)))
                //调用的条件:
                //1.cells 未初始化,即多线程写base发生了竞争[重试 or 初始化cells]
                //2.说明当前线程对应下标的cell为空
                //3.说明cas失败,意味着当前线程对应的cell有竞争[重试 or 扩容cells]
                longAccumulate(x, null, uncontended);
        }
    }
/**
     * 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
     */
    //调用的条件:
    //1.cells 未初始化,即多线程写base发生了竞争[重试 or 初始化cells]
    //2.说明当前线程对应下标的cell为空,需要创建
    //3.说明cas失败,意味着当前线程对应的cell有竞争[重试 or 扩容cells]
    //wasUncontended: 只有cells初始化之后,并且当前线程竞争修改失败,才会是false
    final void longAccumulate(long x, LongBinaryOperator fn, boolean wasUncontended) {
        //h: 表示线程的hash值
        int h;
        //条件成立: 说明当前线程还未分配hash值
        if ((h = getProbe()) == 0) {
            //1.给当前线程分配hash值
            ThreadLocalRandom.current(); // force initialization
            //2.提取当前线程的hash值
            h = getProbe();
            //3.由于默认情况下,当前线程肯定写入到cells[0]位置上,完成步骤2后,会重新分配cells数组的位置给当前线程写入。因此,不当做一次竞争。
            wasUncontended = true;
        }
        //表示扩容意向 false:一定不会扩容 true:可能会扩容
        boolean collide = false;                // True if last slot nonempty

        //for (;;) 表示自旋
        for (;;) {
            //cells: volatile类型的数组,当其它线程修改时,会同步更新到主存
            //as: 引用cells数组
            //a: 当前线程命中的cell
            //n: cells数组长度
            //v: 期望值
            Cell[] as; Cell a; int n; long v;
            //前置条件:当前线程对应下标的cell为空,需要创建 or 当前线程对应的cell有竞争
            //条件1:cells数组已经初始化了,当前线程将数据写入到对应的cell中
            if ((as = cells) != null && (n = as.length) > 0) {
                //条件1.1 true -> 表示当前线程对应下标位置的cell为null,需要new cell
                if ((a = as[(n - 1) & h]) == null) {
                    //true -> 表示当前锁未被占用 false -> 表示当前锁被占用
                    if (cellsBusy == 0) {       // Try to attach new Cell
                        //拿需要计算的值创建cell
                        Cell r = new Cell(x);   // Optimistically create
                        //条件1.1.1 true -> 表示当前锁未被占用 false -> 表示当前锁被占用
                        //条件1.1.2 true -> 表示当前线程获取锁成功 false -> 表示当前线程获取锁失败
                        if (cellsBusy == 0 && casCellsBusy()) {
                            //是否创建成功 标记
                            boolean created = false;
                            try {               // Recheck under lock
                                //rs 表示当前cells数组
                                //m 表示当前cells数组的长度
                                //j 表示当前线程命中的下标
                                Cell[] rs; int m, j;
                                //条件1、2恒成立 cells数组不为空
                                //条件3 rs[j = (m - 1) & h] == null 再次判断当前线程要赋值的cell是否已经被其它线程提前赋值了
                                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
                        }
                    }
                    //强制将扩容意向置为false,因为cells数组中还有cell为创建
                    collide = false;
                }
                //条件1.2 当前线程竞争修改cell失败
                else if (!wasUncontended)       // CAS already known to fail
                    wasUncontended = true;      // Continue after rehash
                //条件1.3 当前线程的hash值进行1次rehash之后,新命中的cell不为null
                //      true -> 写成功,退出自旋
                //      false -> 表示rehash之后新命中的cell也有竞争 重试1次
                else if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x))))
                    break;
                //条件1.4:判断是否可扩容
                //  条件1.4.1:n >= NCPU
                //      true -> cells数组长度已经 >= cpu核数,不可进行扩容,把扩容意向改为false
                //      false -> 可扩容
                //  条件1.4.2:cells != as
                //      true -> 其它线程已经扩容过了,当前线程rehash之后重试即可
                //      false -> 未有线程对cells进行修改
                else if (n >= NCPU || cells != as)
                    //把扩容意向改为false
                    collide = false;            // At max size or stale
                //条件1.5:设置为扩容后,下次再执行到这里就将会进行扩容操作
                //  条件1.5.1:!collide
                //      true -> 设置扩容意向为true,但不一定真的扩容
                else if (!collide)
                    collide = true;
                //条件1.6:真正的扩容逻辑
                //  条件1.6.1:cellsBusy == 0
                //      true -> 表示cells没有被其它线程占用,当前线程可以去竞争锁
                //      false -> 表示有其它线程正在操作cells
                //  条件1.6.2:casCellsBusy()
                //      true -> 表示当前线程获取锁成功,可以进行扩容操作
                //      false -> 表示当前线程获取锁失败,当前时刻有其它线程在做扩容相关的操作
                else if (cellsBusy == 0 && casCellsBusy()) {
                    try {
                        //重复判断一下当前线程的临时cells数组是否与原cells数组一致(防止有其它线程提前修改了cells数组,因为cells是volatile的全局变量)
                        if (cells == as) {      // Expand table unless stale
                            //n << 1 表示数组长度翻一倍
                            Cell[] rs = new Cell[n << 1];
                            for (int i = 0; i < n; ++i)
                                rs[i] = as[i];
                            cells = rs;
                        }
                    } finally {
                        cellsBusy = 0;
                    }
                    //扩容后,将扩容意向置为false
                    collide = false;
                    continue;                   // Retry with expanded table
                }
                //重置当前线程hash值
                h = advanceProbe(h);
            }
            //条件2:前置条件:cells数组未初始化 as 为 null
            //  条件2.1:判断锁是否被占用
            //         true -> 表示当前未加锁
            //         false -> 表示当前已加锁
            //  条件2.2:因为其它线程可能会在当前线程给as赋值之后修改了cells
            //         true -> cells没有被其它线程修改
            //         false -> cells已经被其它线程修改
            // 条件2.3:获取锁
            //         true -> 获取锁成功 会把cellsBusy = 1
            //         false -> 表示其它线程正在持有这把锁
            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;
            }
            //条件3:将数据累加到base中
            //  前置条件:1.cellsBusy被加了锁,表示其它线程在初始化cells,所以当前线程值累加到base
            //           2.cells被其它线程初始化后,当前线程值累加到base (as == null 但 cells != as的情况)
            else if (casBase(v = base, ((fn == null) ? v + x : fn.applyAsLong(v, x))))
                break;                          // Fall back on using base
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值