LongAdder 源码分析

LongAdder

LongAdder 能解决什么问题?什么时候使用 LongAdder?

1)LongAdder 内部包含一个基础值【base】和一个单元【Cell】数组。
没有竞争的情况下,要累加的数会累加到这个基础值上;
如果有竞争的话,LongAdder 会将要累加的数累加到 cell 数组的某个单元里面。
所以整个 LongAdder 的值包括基础值和 Cell 数组中所有单元的值的总和。
2)在竞争不激烈时,其性能类似与 AtomicLong,但是需要更多的存储空间;
在竞争激烈时,其吞吐量要远高于 AtomicLong【以空间换时间】。

如何使用 LongAdder?

1)高并发场景下的多线程计数器

使用 LongAdder 有什么风险?

2)通过 sum 计算结果值时如果存在多线程写入,则其值可能是不精确的。

LongAdder 核心操作的实现原理?

创建实例

    /**
     * 创建一个累积和为 0 的 LongAdder 实例
     */
    public LongAdder() {
    }

累加值

    /**
     * 原子累加指定的值 x 到 LongAdder
     */
    public void add(long x) {
        Cell[] cs; long b, v; int m; Cell c;
        /**
         * 1)如果 cells 为 null,则尝试原子更新值到 base 中
         * 2)如果 cells 不为 null,则将其累加到其中一个 cell 中。
         * if (!casBase(b = base, b + x)) {
         * 首先尝试原子更新值到 base 中,更新失败则将其累加到指定的 cell 中?
         * }
         */
        if ((cs = cells) != null || !casBase(b = base, b + x)) {
            /**
             * 1)cells 为 null,并且原子更新 base 值失败,出现在第一次竞争发生时。
             * 2)cells 不为 null
             * cell 是否发生竞争的标记
             */
            boolean uncontended = true;
            /**
             * cells 不为 null &&
             * 其长度大于 1 &&
             * 基于当前线程的探测值定位的 cell 不为 null &&
             * 则尝试原子更新目标 cell 值
             */
            if (cs == null || (m = cs.length - 1) < 0 ||
                    (c = cs[Striped64.getProbe() & m]) == null ||
                    !(uncontended = c.cas(v = c.value, v + x))) {
                /**
                 * 1)cell 为 null
                 * 2)原子更新目标 cell 值失败,即单个 cell 发生竞争
                 */
                longAccumulate(x, null, uncontended);
            }
        }
    }

Striped64#
    /**
     * 尝试原子更新 base 值
     */
    final boolean casBase(long cmp, long val) {
        return Striped64.BASE.compareAndSet(this, cmp, val);
    }

    final void longAccumulate(long x, LongBinaryOperator fn,
            boolean wasUncontended) {
        int h;
        // 探测值为 0
        if ((h = Striped64.getProbe()) == 0) {
            // 强制初始化当前线程的线程局部随机数
            ThreadLocalRandom.current(); // force initialization
            // 读取新的探测值
            h = Striped64.getProbe();
            // 发生 cell 竞争
            wasUncontended = true;
        }
        boolean collide = false;                // True if last slot nonempty
        done: for (;;) {
            Cell[] cs; Cell c; int n; long v;
            // 1)cells 已经完成初始化
            if ((cs = cells) != null && (n = cs.length) > 0) {
                // 1-1)基于线程探测值定位的 cell 为 null
                if ((c = cs[n - 1 & h]) == null) {
                    // 没有在执行扩容
                    if (cellsBusy == 0) {       // Try to attach new Cell
                        // 创建新的 Cell 并写入值
                        final Cell r = new Cell(x);   // Optimistically create
                        // 原子更新 cellsBusy
                        if (cellsBusy == 0 && casCellsBusy()) {
                            try {               // Recheck under lock
                                Cell[] rs; int m, j;
                                // 再次确认目标 cell 是否为 null
                                if ((rs = cells) != null &&
                                        (m = rs.length) > 0 &&
                                        rs[j = m - 1 & h] == null) {
                                    // 写入新创建的 cell,操作完成
                                    rs[j] = r;
                                    break done;
                                }
                            } finally {
                                // 重置 cellsBusy
                                cellsBusy = 0;
                            }
                            continue;           // Slot is now non-empty
                        }
                    }
                    collide = false;
                }
                /**
                 * 1)基于线程探测值定位的 cell 不为 null
                 * 2)发生 cell 竞争,则重置
                 */
                else if (!wasUncontended) {
                    wasUncontended = true;      // Continue after rehash
                // 尝试原子更新目标 cell 中的值,更新成功则退出循环
                } else if (c.cas(v = c.value,
                        fn == null ? v + x : fn.applyAsLong(v, x))) {
                    break;
                // cells 数组长度超出系统的 CPU 总数或发生 cells 扩容   
                } else if (n >= Striped64.NCPU || cells != cs) {
                    collide = false;            // At max size or stale
                } else if (!collide) {
                    collide = true;
                // 尝试进行扩容
                } else if (cellsBusy == 0 && casCellsBusy()) {
                    try {
                        if (cells == cs) {
                            // 容量扩大为原来的两倍
                            cells = Arrays.copyOf(cs, n << 1);
                        }
                    } finally {
                        cellsBusy = 0;
                    }
                    collide = false;
                    continue;                   // Retry with expanded table
                }
                // 基于伪随机数重新计算探测值
                h = Striped64.advanceProbe(h);
            }
            // 2)未发生 cell 竞争 && cells 未扩容 && 原子更新 cellsBUsy 成功【表示当前 cell 正在写入值】
            else if (cellsBusy == 0 && cells == cs && casCellsBusy()) {
                try {                           // Initialize table
                    if (cells == cs) {
                        // 创建长度为 2 的 Cell 数组
                        final Cell[] rs = new Cell[2];
                        // 将目标值写入指定的 cell
                        rs[h & 1] = new Cell(x);
                        // 更新 cells table
                        cells = rs;
                        break done;
                    }
                } finally {
                    // cells 创建完成,则重置标识
                    cellsBusy = 0;
                }
            }
            // 3)尝试原子更新 base 值
            else if (casBase(v = base,
                    fn == null ? v + x : fn.applyAsLong(v, x))) {
                // 更新成功则退出循环
                break done;
            }
        }
    }

其他操作

    /**
     * 原子累加 1
     */
    public void increment() {
        add(1L);
    }

    /**
     * 原子递减 1
     */
    public void decrement() {
        add(-1L);
    }

    /**
     * 读取 LongAdder 的总和,计算过程中未发生竞争则其值是精确的。
     */
    public long sum() {
        final Cell[] cs = cells;
        long sum = base;
        if (cs != null) {
            for (final Cell c : cs) {
                if (c != null) {
                    sum += c.value;
                }
            }
        }
        return sum;
    }

    /**
     * 只有在确保当前没有多线程竞争时,才应该调用该方法进行重置 LongAdder。
     */
    public void reset() {
        final Cell[] cs = cells;
        base = 0L;
        if (cs != null) {
            for (final Cell c : cs) {
                if (c != null) {
                    c.reset();
                }
            }
        }
    }

posted on 2018-12-02 21:30  竺旭东  阅读(134)  评论(0编辑  收藏  举报

导航