AtomicLong

/**
 * Atomically increments by one the current value.
 *
 * @return the updated value
 */
public final long incrementAndGet() {
    return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}

unsafe

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));
    // 关注重点:if(var2 == var6)
    return var6;
}
  • var1 调用原方法 incrementAndGet 即自身的对象
  • var2 原对象当前(工作内存中的)值
  • var4 要加上去的值
  • var6 调用底层方法 getLongVolatile 获得当前(主内存中的)值,如果没其他线程修改即与 var2 相等

compareAndSwapLong

  • var2var6 为什么可能会不一样?

    • 在并发环境下,工作内存中的值 var2 与主内存中的值 var6 之间的可能不一样(JMM)
// 获取主内存里的值
public native long getLongVolatile(Object var1, long var2);
// CAS 操作
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如 C 和 C++)实现的文件中。Java 语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。

LongAdder

public void increment() {
    add(1L);
}

public void add(long x) {
    Cell[] as; long b, v; int m; Cell a;
    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); // <- 重点
    }
}

Cell

  • Cell 类,是一个普通的二元算术累积单元,它在 Striped64 里面。Striped64 这个类使用分段的思想,来尽量平摊并发压力(类似1.7及以前版本的 ConcurrentHashMap.Segment)。
  • 最终依旧使用 compareAndSwapLong 来更新值。
/**
 * Padded variant of AtomicLong supporting only raw accesses plus CAS.
 *
 * JVM intrinsics note: It would be possible to use a release-only
 * form of CAS here, if it were provided.
 */
@sun.misc.Contended static final class Cell {
    volatile long value;
    Cell(long x) { value = x; }
    final boolean cas(long cmp, long val) {
        return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
    }

    // Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long valueOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> ak = Cell.class;
            valueOffset = UNSAFE.objectFieldOffset
                (ak.getDeclaredField("value"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
}

longAccumulate

  • Long 映射到 Cell[] 数组里面,通过 Hash 等算法映射到其中一个数字进行计数,而最终的计数结果就是其求和累加。在低并发的时候,通过对 base 的直接更新,可以很好地保证和 Atomic 性能的基本一致;而在高并发的时候,则将单点的更新压力分散到各个节点上,提升了性能。

总结

  • AtomicLong 适用于序号生成,这种情况下需要准确的、全局唯一的数值;但在高并发情况下的计数操作,使用 AtomicLong 时会因线程竞争导致失败白白循环一次;失败次数越多,循环次数也越多。此时使用LongAdder 能更好地提升性能。
  • LongAdder 适用于高并发情况下的计数操作,利用与 JDK1.7 ConcurrentHashMap 相似的原理,以空间换时间,提高了实际的计数效率。当然,线程竞争很低的情况下进行计数,使用 AtomicLong 还是更简单更直接,并且效率稍微高一些。

注意:CAS 是 sun.misc.Unsafe 中提供的操作,只对 int、long、对象类型(引用或者指针)提供了这种操作,其他类型都需要转化为这三种类型才能进行 CAS 操作。(例如 DoubleAdder 就是 LongAdder 的简单改造,主要的变化就是用 Double.longBitsToDoubleDouble.doubleToRawLongBits 对底层的8字节数据进行 long <=> double 转换,存储的时候使用 long 型,计算的时候转化为 double 型。)


参考资料


gcusky
135 声望6 粉丝

目标