在java.util.concurrent包下面的很多类为了追求性能都采用了sun.misc.Unsafe类中的CAS操作,从而避免使用synchronized等加锁方式带来性能上的不足。
在sun.misc.Unsafe中CAS方法如下:
1 public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5); 2 3 public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); 4 5 public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
在JDK1.8中只有上述三个CAS方法,其方法参数含义为:var1为待修改的field对象;var2为field对象偏移量,为long型;var4为期望值;var5或var6为替换值,当var1[offset] == var4则设置var1[offset] = var5(var6)。
这三个方法都是native方法,可以查看hotspot源码查看其底层实现:(hotspot/src/share/vm/prims/unsafe.cpp)
1 #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) 2 3 {CC"compareAndSwapObject", CC"("OBJ"J"OBJ""OBJ")Z", FN_PTR(Unsafe_CompareAndSwapObject)}, 4 {CC"compareAndSwapInt", CC"("OBJ"J""I""I"")Z", FN_PTR(Unsafe_CompareAndSwapInt)}, 5 {CC"compareAndSwapLong", CC"("OBJ"J""J""J"")Z", FN_PTR(Unsafe_CompareAndSwapLong)},
1 UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject e_h, jobject x_h)) 2 UnsafeWrapper("Unsafe_CompareAndSwapObject"); 3 oop x = JNIHandles::resolve(x_h); // 更新值 4 oop e = JNIHandles::resolve(e_h); // 期望值 5 oop p = JNIHandles::resolve(obj); // 更新对象 6 HeapWord* addr = (HeapWord *)index_oop_from_field_offset_long(p, offset); // 根据偏移量offset获取内存中的具体位置 7 oop res = oopDesc::atomic_compare_exchange_oop(x, addr, e, true); // 调用方法执行CAS操作 8 jboolean success = (res == e); // 如果返回值res==e则表明满足compare条件,swap成功 9 if (success) 10 update_barrier_set((void*)addr, x); // 更新memory barrier 11 return success; 12 UNSAFE_END 13 14 UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) 15 UnsafeWrapper("Unsafe_CompareAndSwapInt"); 16 oop p = JNIHandles::resolve(obj); 17 jint* addr = (jint *) index_oop_from_field_offset_long(p, offset); 18 return (jint)(Atomic::cmpxchg(x, addr, e)) == e; 19 UNSAFE_END 20 21 UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x)) 22 UnsafeWrapper("Unsafe_CompareAndSwapLong"); 23 Handle p (THREAD, JNIHandles::resolve(obj)); 24 jlong* addr = (jlong*)(index_oop_from_field_offset_long(p(), offset)); 25 if (VM_Version::supports_cx8()) 26 return (jlong)(Atomic::cmpxchg(x, addr, e)) == e; 27 else { 28 jboolean success = false; 29 ObjectLocker ol(p, THREAD); 30 if (*addr == e) { *addr = x; success = true; } 31 return success; 32 } 33 UNSAFE_END
先来看下Unsafe_CompareAndSwapObject方法,该方法通过调用index_oop_from_field_offset_long方法找到需要执行CAS对象的具体地址,然后调用atomic_compare_exchange_oop方法执行CAS操作。
继续深入atomic_compare_exchange_oop方法看一下,源码如下
1 // 声明在hotspot/src/share/vm/oops/oop.hpp 2 static oop atomic_compare_exchange_oop(oop exchange_value, 3 volatile HeapWord *dest, 4 oop compare_value, 5 bool prebarrier = false); 6 7 // 定义在hotspot/src/share/vm/oops/oop.inline.hpp 8 inline oop oopDesc::atomic_compare_exchange_oop(oop exchange_value, 9 volatile HeapWord *dest, 10 oop compare_value, 11 bool prebarrier) { 12 if (UseCompressedOops) { 13 if (prebarrier) { 14 update_barrier_set_pre((narrowOop*)dest, exchange_value); 15 } 16 // encode exchange and compare value from oop to T 17 narrowOop val = encode_heap_oop(exchange_value); 18 narrowOop cmp = encode_heap_oop(compare_value); 19 20 narrowOop old = (narrowOop) Atomic::cmpxchg(val, (narrowOop*)dest, cmp); 21 // decode old from T to oop 22 return decode_heap_oop(old); 23 } else { 24 if (prebarrier) { 25 update_barrier_set_pre((oop*)dest, exchange_value); 26 } 27 return (oop)Atomic::cmpxchg_ptr(exchange_value, (oop*)dest, compare_value); 28 } 29 }
在atomic_compare_exchange_oop方法中,核心的CAS操作最终是调用了Atomic::cmpxchg(val, (narrowOop*)dest, cmp)函数或者Atomic::cmpxchg_ptr(exchange_value, (oop*)dest, compare_value)函数。
Atomic::cmpxchg(val, (narrowOop*)dest, cmp)函数虽然有很多重载函数,但最终都是调用的下面的函数:
1 // hotspot/src/share/vm/runtime/Atomic.cpp 2 jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value) { 3 assert(sizeof(jbyte) == 1, "assumption."); 4 uintptr_t dest_addr = (uintptr_t)dest; 5 uintptr_t offset = dest_addr % sizeof(jint); 6 volatile jint* dest_int = (volatile jint*)(dest_addr - offset); 7 jint cur = *dest_int; // 对象当前值 8 jbyte* cur_as_bytes = (jbyte*)(&cur); // 当前值cur的地址 9 jint new_val = cur; 10 jbyte* new_val_as_bytes = (jbyte*)(&new_val); // new_val地址 11 // new_val存exchange_value,后面修改则直接从new_val中取值 12 new_val_as_bytes[offset] = exchange_value; 13 // 比较当前值与期望值,如果相同则更新,不同则直接返回 14 while (cur_as_bytes[offset] == compare_value) { 15 // 调用汇编指令cmpxchg执行CAS操作,期望值为cur,更新值为new_val 16 jint res = cmpxchg(new_val, dest_int, cur); 17 if (res == cur) break; 18 cur = res; 19 new_val = cur; 20 new_val_as_bytes[offset] = exchange_value; 21 } 22 // 返回当前值 23 return cur_as_bytes[offset]; 24 }
Atomic::cmpxchg_ptr(exchange_value, (oop*)dest, compare_value)函数在不同系统中都有各自的声明,但是最终都是调用的下面的函数:
1 // hotspot/src/os_cpu/solaris_x86/vm/Atomic_solaris_x86.inline.hpp 2 3 // This is the interface to the atomic instruction in solaris_i486.s. 4 jlong _Atomic_cmpxchg_long_gcc(jlong exchange_value, volatile jlong* dest, jlong compare_value, int mp); 5 6 inline jlong _Atomic_cmpxchg_long(jlong exchange_value, volatile jlong* dest, jlong compare_value, int mp) { 7 #ifdef AMD64 8 __asm__ __volatile__ (LOCK_IF_MP(%4) "cmpxchgq %1,(%3)" 9 : "=a" (exchange_value) 10 : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp) 11 : "cc", "memory"); 12 return exchange_value; 13 #else 14 return _Atomic_cmpxchg_long_gcc(exchange_value, dest, compare_value, os::is_MP()); 15 16 #if 0 17 // The code below does not work presumably because of the bug in gcc 18 // The error message says: 19 // can't find a register in class BREG while reloading asm 20 // However I want to save this code and later replace _Atomic_cmpxchg_long_gcc 21 // with such inline asm code: 22 23 volatile jlong_accessor evl, cvl, rv; 24 evl.long_value = exchange_value; 25 cvl.long_value = compare_value; 26 int mp = os::is_MP(); 27 28 __asm__ volatile ("cmp $0, %%esi/n/t" 29 "je 1f /n/t" 30 "lock/n/t" 31 "1: cmpxchg8b (%%edi)/n/t" 32 : "=a"(cvl.words[0]), "=d"(cvl.words[1]) 33 : "a"(cvl.words[0]), "d"(cvl.words[1]), 34 "b"(evl.words[0]), "c"(evl.words[1]), 35 "D"(dest), "S"(mp) 36 : "cc", "memory"); 37 return cvl.long_value; 38 #endif // if 0 39 #endif // AMD64 40 }
在这个方法中废弃了32位系统的cmpxchg8b指令实现CAS操作方式,只提供了AMD64位操作系统cmpxchgq指令实现方式。
从上面可以看出无论是Atomic::cmpxchg(val, (narrowOop*)dest, cmp)函数或者Atomic::cmpxchg_ptr(exchange_value, (oop*)dest, compare_value)函数,二者最终都是通过一条汇编指令实现CAS操作的。
Unsafe_CompareAndSwapInt和Unsafe_CompareAndSwapLong两个方法都是调用Atomic::cmpxchg(val, (narrowOop*)dest, cmp)函数实现的,这个函数上面已经解释过。
综合上面的源码分析,可以知道sun.misc.Unsafe类中的CAS都是通过一条汇编指令实现的,这也就不难理解为什么这个操作可以保证原子性了。
参考文章:
http://blog.csdn.net/qqqqq1993qqqqq/article/details/75211993
https://www.cnblogs.com/dennyzhangdd/p/6734933.html
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/11346.html