目录
六、AtomicMarkableReference 和 AtomicStampedReference
atomic包提供了Boolean,Integer,Long和对象引用类型的值的原子更新的工具类,还提供了Double和Long类型的原子累加器实现,前者的实现是基于Unsafe类提供的方法,后者的实现是基于Striped64类,本篇博客就以其中的典型类为例来说明其具体的使用方式和实现细节。
一、AtomicLong
AtomicLong只有一个实例属性value,用volatile修饰,有两个静态属性Unsafe实例和表示value属性偏移量的valueOffset属性,后者通过静态代码块赋值,如下:
重点关注以下方法的实现:
//修改该值,修改后对其他CPU立即可见
public final void lazySet(long newValue) {
unsafe.putOrderedLong(this, valueOffset, newValue);
}
//底层会原子的修改为目标值并返回原来的值
public final long getAndSet(long newValue) {
return unsafe.getAndSetLong(this, valueOffset, newValue);
}
//如果当前值是expect,则更新成update,如果更新成功返回true
public final boolean compareAndSet(long expect, long update) {
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
//将当前值原子的加上delta,并返回加之前的值
public final long getAndAdd(long delta) {
return unsafe.getAndAddLong(this, valueOffset, delta);
}
上述Unsafe的方法实现如下:
//实现Unsafe的putOrderedLong方法
UNSAFE_ENTRY(void, Unsafe_SetOrderedLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong x))
UnsafeWrapper("Unsafe_SetOrderedLong");
#ifdef SUPPORTS_NATIVE_CX8
SET_FIELD_VOLATILE(obj, offset, jlong, x);
#
UNSAFE_END
#define SET_FIELD_VOLATILE(obj, offset, type_name, x) \
oop p = JNIHandles::resolve(obj); \
//将其作为一个volatile变量指针,修改值并通过内存屏障指令强制写回内存,让其他CPU都可以看见
OrderAccess::release_store_fence((volatile type_name*)index_oop_from_field_offset_long(p, offset), truncate_##type_name(x));
//根据字段偏移量计算该属性的内存地址
inline void* index_oop_from_field_offset_long(oop p, jlong field_offset) {
jlong byte_offset = field_offset_to_byte_offset(field_offset);
if (sizeof(char*) == sizeof(jint)) // (this constant folds!)
return (address)p + (jint) byte_offset;
else
return (address)p + byte_offset;
}
inline jlong field_offset_to_byte_offset(jlong field_offset) {
return field_offset;
}
#define truncate_jlong(x) (x)
public final long getAndSetLong(Object o, long offset, long newValue) {
long v;
do {
//获取最新值
v = getLongVolatile(o, offset);
} while (!compareAndSwapLong(o, offset, v, newValue)); //如果当前值是v则更新成newValue,更新成功返回true,否则返回false,通过while循环继续修改
return v;
}
//GetLongVolatile方法通过宏定义实现的,实现Unsafe的getLongVolatile方法
#ifdef SUPPORTS_NATIVE_CX8
DEFINE_GETSETOOP_VOLATILE(jlong, Long);
#endif
//这个宏同时定了Get和Set方法
#define DEFINE_GETSETOOP_VOLATILE(jboolean, Boolean) \
\
UNSAFE_ENTRY(jboolean, Unsafe_Get##Boolean##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) \
UnsafeWrapper("Unsafe_Get"#Boolean); \
GET_FIELD_VOLATILE(obj, offset, jboolean, v); \
return v; \
UNSAFE_END \
\
UNSAFE_ENTRY(void, Unsafe_Set##Boolean##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jboolean x)) \
UnsafeWrapper("Unsafe_Set"#Boolean); \
SET_FIELD_VOLATILE(obj, offset, jboolean, x); \
UNSAFE_END \
\
#define GET_FIELD_VOLATILE(obj, offset, type_name, v) \
//从JNI引用中解析出oop
oop p = JNIHandles::resolve(obj); \
if (support_IRIW_for_not_multiple_copy_atomic_cpu) { \
OrderAccess::fence(); \
} \
//基于内存屏障指令,读取该地址最新的值
volatile type_name v = OrderAccess::load_acquire((volatile type_name*)index_oop_from_field_offset_long(p, offset));
//用于实现Unsafe的compareAndSwapLong方法
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x))
UnsafeWrapper("Unsafe_CompareAndSwapLong");
Handle p (THREAD, JNIHandles::resolve(obj));
//获取属性地址
jlong* addr = (jlong*)(index_oop_from_field_offset_long(p(), offset));
//通过cmpxchg和lock指令前缀实现,会比较当前值与期望值是否一致,如果一致则更新,否则返回当前值
return (jlong)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END
//Unsafe的getAndAddLong方法实现
public final long getAndAddLong(Object o, long offset, long delta) {
long v;
do {
//获取最新值
v = getLongVolatile(o, offset);
} while (!compareAndSwapLong(o, offset, v, v + delta));//原子的修改,修改失败返回false,继续重试,否则返回true
return v;
}
二、AtomicLongArray
AtomicLongArray定义的方法和实现和AtomicLong是一样的,最大的区别在于后者需要根据修改的数组索引值来计算对应索引的数组元素的内存地址,而不是固定的value属性偏移量了,其计算某个索引的内存地址的实现如下,以set方法为例说明:
public final void set(int i, long newValue) {
//putLongVolatile对应的实现是Unsafe_SetLongVolatile,还是通过SET_FIELD_VOLATILE宏实现
//即跟putOrderedLong的实现是一样的
unsafe.putLongVolatile(array, checkedByteOffset(i), newValue);
}
//相对于array的偏移量
private long checkedByteOffset(int i) {
if (i < 0 || i >= array.length)
throw new IndexOutOfBoundsException("index " + i);
return byteOffset(i);
}
private static long byteOffset(int i) {
//i<<shift实际是i*2^shift,用于计算前i个元素的内存大小
return ((long) i << shift) + base;
}
//base不是基地址,而是数组在内存中的表示,有一部字节是固定,用来记录数组长度和元素类型的
//base返回这部分内存的字节数,实际的数组元素存储在该部分字节的后面
private static final int base = unsafe.arrayBaseOffset(long[].class);
static {
//scale表示一个数组元素的字节数,long下scale就是8
int scale = unsafe.arrayIndexScale(long[].class);
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
//shift的结果就是3
shift = 31 - Integer.numberOfLeadingZeros(scale);
}
三、AtomicLongFieldUpdater
AtomicLongFieldUpdater用于给一个volatile long类型的属性做原子更新,实际使用过程中如果直接将某个volatile long类型的属性声明成AtomicLong类型,则在读该变量的时候都会增加额外的代码,因为volatile关键字本身会保证CPU读取该变量时永远是最新的,volatile关键字不能保证修改是原子的,就可以使用AtomicLongFieldUpdater来完成修改。
其使用如下:
class Holder {
//注意不能是static属性,否则获取属性偏移量时报错
private volatile long test = 1;
AtomicLongFieldUpdater<Holder> updater=AtomicLongFieldUpdater.newUpdater(Holder.class, "test");
public long getTest() {
return te