目前JAVA除了使用synchronized关键字来控制进行共享数据的同步外,还引入了轻量级并发框架,以使得JAVA可以以优雅的面向对象的方式对共享数据进行同步,该并发框架在集合等框架中被广泛使用,而轻量级并发的实现原理就是CAS,所以深入理解CAS的实现,攻克轻量级并发这个山头堡垒,对于正确使用JAVA并发框架非常重要,所以本文蜻蜓点水大概了解下CAS的实现,不详细分析每一行代码,主要这种分析CAS实现的大概细节,有关指针,汇编等的细节,还请参考相关书籍,不正确地方指出,还望指出。
在sun.misc包中,有这样一个类,Unsafe.java,这是java调用轻量级锁的入口,这里面大部分方法被声明为native方法,表明这里的功能大部分依赖底层的平台特性实现,该类位于
openjdk/jdk/src/share/classes/sun/misc/目录下,部分代码片段如下:
package sun.misc;
import java.security.*;
import java.lang.reflect.*;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
/**
* A collection of methods for performing low-level, unsafe operations.
* Although the class and all methods are public, use of this class is
* limited because only trusted code can obtain instances of it.
*
* @author John R. Rose
* @see #getUnsafe
*/
public final class Unsafe {
private static native void registerNatives();
static {
registerNatives();
sun.reflect.Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe");
}
private Unsafe() {}
private static final Unsafe theUnsafe = new Unsafe();
@CallerSensitive
public static Unsafe getUnsafe() {
Class cc = Reflection.getCallerClass();
if (cc.getClassLoader() != null)
throw new SecurityException("Unsafe");
return theUnsafe;
}
/**
* Atomically update Java variable to x if it is currently
* holding expected.
* @return true if successful
*/
public final native boolean compareAndSwapObject(Object o, long offset,
Object expected,
Object x);
// 其他代码忽略
}
public static Unsafe getUnsafeInstance() throws Exception {
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
return (Unsafe) theUnsafeField.get(Unsafe.class);
}
/**
* Atomically update Java variable to x if it is currently
* holding expected.
* @return true if successful
*/
public final native boolean compareAndSwapObject(Object o, long offset,
Object expected,
Object x);
参数说明:
o:需要操作字段的对象
expected:期望的对象
x:需要交换的值
offset:需要操作的对象相对于o对象的地址偏移量,通过调用Unsafe.objectFieldOffset(Field field)获取,比如ConcurrentHashMap.HashEntry对next字段偏移量的获取
static final class HashEntry {
// 其他代码忽略
volatile HashEntry next;
// Unsafe mechanics
static final sun.misc.Unsafe UNSAFE;
static final long nextOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class k = HashEntry.class;
nextOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("next"));
} catch (Exception e) {
throw new Error(e);
}
}
}
jvm底层的代码都使用C++实现,轻量级锁也是如此,unsafe.cpp位于hostspot/src/share/vm/prims下,部分代码片段如下:
#define UNSAFE_ENTRY(result_type, header) \
JVM_ENTRY(result_type, header)
#define UNSAFE_END JVM_END
// JSR166 ------------------------------------------------------------------
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) {
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;
UNSAFE_END
上面代码分析如下,
将java的对象解析为JVM的oop(ordinary object pointer,普通对象指针,而不是面向对象编程),解析方法为jniHandles.hpp(hostspot/src/share/vm/runtime/)文件中,
代码如下:
class JNIHandles : AllStatic {
public:
// Resolve handle into oop
inline static oop resolve(jobject handle);
};
inline oop JNIHandles::resolve(jobject handle) {
oop result = (handle == NULL ? (oop)NULL : *(oop*)handle);
assert(result != NULL || (handle == NULL || !CheckJNICalls || is_weak_global_handle(handle)), "Invalid value read from jni handle");
assert(result != badJNIHandle, "Pointing to zapped jni handle area");
return result;
};
为提高调用效率,方法被声明为内联方法,该方法就是将java对象,转换为JVM的oop
将对象o转换为堆地址,通过对象o和地址偏移量确定,通过调用方法index_oop_from_field_offset_long:
inline void* index_oop_from_field_offset_long(oop p, jlong field_offset) {
jlong byte_offset = field_offset_to_byte_offset(field_offset);
// Don't allow unsafe to be used to read or write the header word of oops
assert(p == NULL || field_offset >= oopDesc::header_size(), "offset must be outside of header");
#ifdef ASSERT
if (p != NULL) {
assert(byte_offset >= 0 && byte_offset <= (jlong)MAX_OBJECT_SIZE, "sane offset");
if (byte_offset == (jint)byte_offset) {
void* ptr_plus_disp = (address)p + byte_offset;
assert((void*)p->obj_field_addr((jint)byte_offset) == ptr_plus_disp,
"raw [ptr+disp] must be consistent with oop::field_base");
}
jlong p_size = HeapWordSize * (jlong)(p->size());
assert(byte_offset < p_size, err_msg("Unsafe access: offset " INT64_FORMAT " > object's size " INT64_FORMAT, byte_offset, p_size));
}
#endif
if (sizeof(char*) == sizeof(jint)) // (this constant folds!)
return (address)p + (jint) byte_offset;
else
return (address)p + byte_offset;
}
从方法可看出,需要操作的对象的地址为指针p+偏移量,然后强制转换为HeapWord指针,因为方法index_oop_from_field_offset_long
oopDesc::atomic_compare_exchange_oop(x, addr, e)方法交换对象,该方法在oop.hpp中进行声明
class oopDesc {
// 其他代码忽略
public:
static oop atomic_compare_exchange_oop(oop exchange_value,
volatile HeapWord *dest,
oop compare_value);
}
然后在oop.inline.cpp(hostspot/src/share/vm/oops)中提供实现
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);
}
}
Atomic.hpp(hostspot/src/share/vm/runtime/)中代码如下:
#ifndef SHARE_VM_RUNTIME_ATOMIC_HPP
#define SHARE_VM_RUNTIME_ATOMIC_HPP
#include "memory/allocation.hpp"
class Atomic : AllStatic {
public:
// Performs atomic exchange of *dest with exchange_value. Returns old prior value of *dest.
static jint xchg(jint exchange_value, volatile jint* dest);
static unsigned int xchg(unsigned int exchange_value,
volatile unsigned int* dest);
static intptr_t xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest);
static void* xchg_ptr(void* exchange_value, volatile void* dest);
// Performs atomic compare of *dest and compare_value, and exchanges *dest with exchange_value
// if the comparison succeeded. Returns prior value of *dest. Guarantees a two-way memory
// barrier across the cmpxchg. I.e., it's really a 'fence_cmpxchg_acquire'.
static jbyte cmpxchg (jbyte exchange_value, volatile jbyte* dest, jbyte compare_value);
static jint cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value);
// See comment above about using jlong atomics on 32-bit platforms
static jlong cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value);
static unsigned int cmpxchg(unsigned int exchange_value,
volatile unsigned int* dest,
unsigned int compare_value);
static intptr_t cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value);
static void* cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value);
};
#endif // SHARE_VM_RUNTIME_ATOMIC_HPP
各种实现的文件都位于hostspot/src/os_cpu/中,winidows的则为:atomic_windows_x86.inline.hpp(hostspot/src/os_cpu/windows_x86/vm/),其中的两个方法实现代码如下,本质是通过CAS指令,通过嵌入汇编语言实现:
// Adding a lock prefix to an instruction on MP machine
// VC++ doesn't like the lock prefix to be on a single line
// so we can't insert a label after the lock prefix.
// By emitting a lock prefix, we can define a label after it.
#define LOCK_IF_MP(mp) __asm cmp mp, 0 \
__asm je L0 \
__asm _emit 0xF0 \
__asm L0:
#ifdef AMD64
inline jint Atomic::xchg(jint exchange_value, volatile jint* dest) {
// alternative for InterlockedExchange
__asm {
mov eax, exchange_value;
mov ecx, dest;
xchg eax, dword ptr [ecx];
}
}
inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) {
return (intptr_t)xchg((jint)exchange_value, (volatile jint*)dest);
}
inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) {
return (void*)xchg((jint)exchange_value, (volatile jint*)dest);
}
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {
// alternative for InterlockedCompareExchange
int mp = os::is_MP();
__asm {
mov edx, dest
mov ecx, exchange_value
mov eax, compare_value
LOCK_IF_MP(mp)
cmpxchg dword ptr [edx], ecx
}
}
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]
pop edi
pop ebx
}
}
inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) {
return (void*)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value);
}
inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value) {
// alternative for InterlockedCompareExchange
int mp = os::is_MP();
__asm {
mov edx, dest //表示把目标地址放入edx寄存器中
mov ecx, exchange_value //把exchange_value放入寄存器ecx中
mov eax, compare_value // 需要和目标之地址指向的值进行比较的值放eax寄存器中
LOCK_IF_MP(mp)
cmpxchg dword ptr [edx], ecx // 执行比较操作并交换,这是一个原子操作
}
}
其中,dword ptr [edx]表示取目标地址的指向的值,由于edx的值只是一个指针,指针指向的存储单元的值和ecx宽度可能不同,所以需要明确指出dword
上述汇编代码执行后,dest指向的内存中的值为exchange_value。
inline jlong Atomic::cmpxchg(jlong exchange_value, volatile jlong* dest, jlong compare_value) {
bool mp = os::is_MP();
__asm__ __volatile__ (LOCK_IF_MP(%4) "cmpxchgq %1,(%3)"
: "=a" (exchange_value)
: "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
: "cc", "memory");
return exchange_value;
}