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
-
var2
与var6
为什么可能会不一样?- 在并发环境下,工作内存中的值
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.7ConcurrentHashMap
相似的原理,以空间换时间,提高了实际的计数效率。当然,线程竞争很低的情况下进行计数,使用AtomicLong
还是更简单更直接,并且效率稍微高一些。
注意:CAS 是 sun.misc.Unsafe
中提供的操作,只对 int、long、对象类型(引用或者指针)提供了这种操作,其他类型都需要转化为这三种类型才能进行 CAS 操作。(例如 DoubleAdder
就是 LongAdder
的简单改造,主要的变化就是用 Double.longBitsToDouble
和 Double.doubleToRawLongBits
对底层的8字节数据进行 long <=> double
转换,存储的时候使用 long 型,计算的时候转化为 double 型。)
参考资料
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。