上次讲到在无锁编程中,AtomicInteger可以对int型变量进行原子加减等操作,但是如果操作的是变量类型变成一个复杂对象,那么AtomicStampedReference就派上用场了,下面还是通过compareAndSet方法来剖析一下其内部的机制(以32位X86平台为例)。
AtomicStampedReference::compareAndSet-> AtomicStampedReference::casPair-> UNSAFE::compareAndSwapObject-> Unsafe_CompareAndSwapObject-> atomic_compare_exchange_oop-> Atomic ::cmpxchg_ptr-> Atomic:: cmpxchg
// // 可以看到比AtomicInteger中compareAndSet多了两个参数,目的是为了解决著名的ABA问题
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp))); // 只是casPair成功还不行,stamp 还必须相等,为了防止ABA问题
}
// 简单的调用,掠过
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject e_h, jobject x_h))
UnsafeWrapper("Unsafe_CompareAndSwapObject");
oop x = JNIHandles::resolve(x_h);
oop e = JNIHandles::resolve(e_h);
oop p = JNIHandles::resolve(obj);
HeapWord* addr = (HeapWord *)index_oop_from_field_offset_long(p, offset);
if (UseCompressedOops) { // UseCompressedOops是64位系统对象压缩的情况
update_barrier_set_pre((narrowOop*)addr, e);
} else {
update_barrier_set_pre((oop*)addr, e);
}
oop res = oopDesc::atomic_compare_exchange_oop(x, addr, e);
jboolean success = (res == e);
if (success)
update_barrier_set((void*)addr, x);
return success;
inline oop oopDesc::atomic_compare_exchange_oop(oop exchange_value,
volatile HeapWord *dest,
oop compare_value) {
if (UseCompressedOops) {
// encode exchange and compare value from oop to T
narrowOop val = encode_heap_oop(exchange_value);
narrowOop cmp = encode_heap_oop(compare_value);
narrowOop old = (narrowOop) Atomic::cmpxchg(val, (narrowOop*)dest, cmp);
// decode old from T to oop
return decode_heap_oop(old);
} else {
return (oop)Atomic::cmpxchg_ptr(exchange_value, (oop*)dest, compare_value); // 这里显然走的这个路径
}
}
inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) {
return (void*)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value); // 注意这里cmpxchg函数交换的是jlong,64位字长
}
inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value) {
int mp = os::is_MP();
jint ex_lo = (jint)exchange_value;
jint ex_hi = *( ((jint*)&exchange_value) + 1 );
jint cmp_lo = (jint)compare_value;
jint cmp_hi = *( ((jint*)&compare_value) + 1 );
__asm {
push ebx
push edi
mov eax, cmp_lo
mov edx, cmp_hi
mov edi, dest
mov ebx, ex_lo
mov ecx, ex_hi
LOCK_IF_MP(mp)
cmpxchg8b qword ptr [edi] // cmpxchg8b 指令是32 x86后来加上的,目的是为了一次交换8个字节长度,现在的cpu几乎都支持了
pop edi
pop ebx
}
有了注释和之前的一些基础,上面的代码应该能粗略理解,大家可以看到,复杂对象的原子交换要考虑到ABA问题,一次交换8个字节,其中4个直接是对象指针,还有4个字节实际上就是stamp,这个stamp就是解决ABA问题的关键。
要深入理解以上内容,必须理解无锁编程中的ABA问题,了解此问题的一般解决方法,鉴于这方面的内容、资料非常之多,这里我就不赘述了。
使用VC++的同学注意下,vista以上的版本才支持InterlockedCompareExchange64这个函数,而如果你想在XP中使用8字节交换,可以参照Atomic::cmpxchg实现自己的InterlockedCompareExchange64。