理解Java中的AtomicLong类

由于Java的原子操作最多只支持32位的,而longdouble是64位的,因此它们不具有原子性。

而在java中定义了AtomicLong这个类,来实现原子的long类型。那具体是怎么实现的?我们先来了解下CAS。

Compare And Swap(CAS)

CAS的原理是拿期望的值和原本的一个值作比较,如果相同则更新为新的值,此处“原本的一个值”怎么来的,我们先看看AtomicLong类的几个实现:

1
2
3
4
5
6
7
8
9
10
//value值的偏移量
private static final long valueOffset;

//拿到value原来的值
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicLong.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}

这里用到UnSafe类的一个方法objectFieldOffset()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/** 
* Report the location of a given static field, in conjunction with {@link
* #staticFieldBase}.
* <p>Do not expect to perform any sort of arithmetic on this offset;
* it is just a cookie which is passed to the unsafe heap memory accessors.
*
* <p>Any given field will always have the same offset, and no two distinct
* fields of the same class will ever have the same offset.
*
* <p>As of 1.4.1, offsets for fields are represented as long values,
* although the Sun JVM does not use the most significant 32 bits.
* It is hard to imagine a JVM technology which needs more than
* a few bits to encode an offset within a non-array object,
* However, for consistency with other methods in this class,
* this method reports its result as a long value.
* @see #getInt(Object, long)
*/
public native long objectFieldOffset(Field f);

这个方法是用来拿到我们上文提到的“原本的一个值”的内存地址。是一个本地方法,返回值是valueOffset。它的参数field就是AtomicLong里定义的value属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private volatile long value;

/**
* Creates a new AtomicLong with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicLong(long initialValue) {
value = initialValue;
}

/**
* Creates a new AtomicLong with initial value {@code 0}.
*/
public AtomicLong() {
}

value是由volatile修饰的变量,在内存中可见,因此可以保证任何时刻任何线程都能拿到该变量的最新值。此处value的值,可以在AtomicLong类初始化的时候传入,也可以留空,留空则自动赋值为0

然后我们再看下incrementAndGet()这个方法是如何实现CAS的:

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

这个方法又调用了unsafegetAndAddLong()

1
2
3
4
5
6
7
8
9
public final long getAndAddLong(Object o, long offset, long delta) {
long v;
do {
//拿到原来的变量值,如果没有初始化,默认为0
v = this.getLongVolatile(o, offset);
} while(!this.compareAndSwapLong(o, offset, v, v + delta));

return v;
}

再继续看compareAndSwapLong()方法:

1
2
3
4
5
6
7
8
9
10
/**
* 比较obj的offset处内存位置中的值和期望的值,如果相同则更新。此更新是不可中断的。
*
* @param obj 需要更新的对象
* @param offset obj中整型field的偏移量
* @param expect 期望值
* @param update 如果期望值expect与field的当前值相同,设置filed的值为这个新值
* @return 如果field的值被更改返回true
*/
public final native boolean compareAndSwapLong(Object obj, long offset, long expect, long update);

我们知道,如果我们创建AtomicLong实例时不传入参数,则原始变量的值即为0。所以上面v = this.getLongVolatile(o, offset);拿到的值为0。而this.compareAndSwapLong()这个方法是Java本地的方法,由于Java语言无法访问到操作系统底层(如硬件等),所以Java使用native来扩展功能。这个CAS实现具体由C或C++来完成的。

大概的实现原理是:有3个操作数,内存值M,预期值E,新值U,如果M==E,则将内存值修改为U,返回true,否则返回false。如果为false,会继续拿期望的值与offset原始值做比较,知道成功为止。

这篇文章简单的模拟一个CAS实现,当然这不是真正的CAS实现,真正的CAS是CPU指令来实现的。