在Java中CAS是JUC的基石之一,HotSpot通过UnSafe类来实现CAS。CAS语义:如果当前状态值等于预期值,则以原子方式同步状态设置为给定的更新值,从volatile的角度来看CAS需要同时具备volatile读写内存语义。编译器不会对volatile写以及volatile写前面的指令重排序,要同时实现volatile读和volatile写的内存语义,编译器不能对CAS与CAS前面和后面的任意指令做重排序。
一.AtomicInteger
AtomicInteger是JUC提供的一个原子类,AtomicInteger通过一个volatile int value变量,借助UnSafe类来实现原子操作,比如getAndIncrement()方法。
java/util/concurrent/atomic/AtomicInteger.java
public class AtomicInteger extends Number implements java.io.Serializable {
//UnSafe类
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
//通过UnSafe类拿到AtomicInteger的value变量的在内存中的相对偏移
private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");
//value变量
private volatile int value;
//原子自增
public final int getAndIncrement() {
return U.getAndAddInt(this, VALUE, 1);
}
}
jdk/internal/misc/Unsafe.java
- o: AtomicInteger类
- offset: value变量的在内存中的相对偏移
- delta: 自增量
public final class Unsafe {
@HotSpotIntrinsicCandidate
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset); //通过偏移获取value
} while (!weakCompareAndSetInt(o, offset, v, v + delta)); //执行CAS直到更新成功
return v;
}
}
jdk/internal/misc/Unsafe.java
getIntVolatile是一个native方法,compareAndSetInt也是一个native方法
public final class Unsafe {
@HotSpotIntrinsicCandidate
public native int getIntVolatile(Object o, long offset);
@HotSpotIntrinsicCandidate
public final boolean weakCompareAndSetInt(Object o, long offset,
int expected,
int x) {
return compareAndSetInt(o, offset, expected, x);
}
@HotSpotIntrinsicCandidate
public final native boolean compareAndSetInt(Object o, long offset,
int expected,
int x);
}
二.Unsafe
对compareAndSetInt方法Unsafe类中使用DECLARE_GETPUTOOP来展开为Unsafe_GetObjectVolatile方法
src/share/vm/prims/unsafe.cpp
//针对原始类型的宏
#define DECLARE_GETPUTOOP(Type, Desc) \
{CC "get" #Type, CC "(" OBJ "J)" #Desc, FN_PTR(Unsafe_Get##Type)}, \
{CC "put" #Type, CC "(" OBJ "J" #Desc ")V", FN_PTR(Unsafe_Put##Type)}, \
{CC "get" #Type "Volatile", CC "(" OBJ "J)" #Desc, FN_PTR(Unsafe_Get##Type##Volatile)}, \
{CC "put" #Type "Volatile", CC "(" OBJ "J" #Desc ")V", FN_PTR(Unsafe_Put##Type##Volatile)}
static JNINativeMethod jdk_internal_misc_Unsafe_methods[] = {
//DECLARE_GETPUTOOP展开后getIntVolatile方法对应Unsafe_GetObjectVolatile方法
DECLARE_GETPUTOOP(Int, I),
//compareAndSetInt直接对应Unsafe_CompareAndSetInt方法
{CC "compareAndSetInt", CC "(" OBJ "J""I""I"")Z", FN_PTR(Unsafe_CompareAndSetInt)},
}
Unsafe_GetObjectVolatile从内存中读volatile变量value的值,在volatile原理一篇中,HotSpot只对volatile变量写操作插入了内存屏障来保证多线程下,volatile变量对其他线程可见,对于volatile变量读并没有插入屏障操作,但是在Unsafe_GetObjectVolatile中对AtomicInteger读volatile变量value,通过OrderAccess::fence()来强刷value到主存,来保证读到的value为最新的
src/share/vm/prims/unsafe.cpp
UNSAFE_ENTRY(jobject, Unsafe_GetObjectVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) {
//解析类获取AtomicInteger在HotSpot中的内部表示oop
oop p = JNIHandles::resolve(obj);
//从oop模型中拿到AtomicInteger中value的内存地址
void* addr = index_oop_from_field_offset_long(p, offset);
//c++的volatile阻止编译器优化
volatile oop v;
if (support_IRIW_for_not_multiple_copy_atomic_cpu) { //支持多CPU原子读写
OrderAccess::fence(); //强制刷新到主内存
}
if (UseCompressedOops) {
//转换成int在HotSpot中的表示,value是int类型
volatile narrowOop n = *(volatile narrowOop*) addr;
(void)const_cast<oop&>(v = oopDesc::decode_heap_oop(n));
} else {
(void)const_cast<oop&>(v = *(volatile oop*) addr);
}
ensure_satb_referent_alive(p, offset, v);
OrderAccess::acquire();
return JNIHandles::make_local(env, v); //返回一个Java对象即value
} UNSAFE_END
关于 OrderAccess::fence(),X86架构下提供了一组用于实现内存屏障的代码
src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp
static inline void compiler_barrier() {
__asm__ volatile ("" : : : "memory");
}
......
inline void OrderAccess::release() { compiler_barrier(); }
//fence()中同样是通过X86的lock指令来实现
inline void OrderAccess::fence() {
if (os::is_MP()) {
// always use locked addl since mfence is sometimes expensive
#ifdef AMD64
__asm__ volatile ("lock; addl $0,0(%%rsp)" : : : "cc", "memory");
#else
__asm__ volatile ("lock; addl $0,0(%%esp)" : : : "cc", "memory");
#endif
}
compiler_barrier();
}
三.CAS
src/share/vm/prims/unsafe.cpp
- offset : value偏移
- e:getIntVolatile读到的最新值,也即期望值
- x:要更新的值即v + delta的值
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) {
oop p = JNIHandles::resolve(obj); //AtomicInteger在HotSpot中的表示
//从oop模型中拿到AtomicInteger中value的内存地址
jint* addr = (jint *)index_oop_from_field_offset_long(p, offset);
//执行比较交换,返回值与期望值相等表示更新成功,否则更新失败
return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
} UNSAFE_END
src/os_cpu/linux_x86/vm/atomic_linux_x86.hpp
比较dest处的值与compare_value值是否相等,相等则说明未被更改,则将新值exchange_value更新到dest,返回exchange_value
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value, cmpxchg_memory_order order) {
int mp = os::is_MP();
__asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)"
: "=a" (exchange_value)
: "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
: "cc", "memory");
return exchange_value;
}
src/os_cpu/linux_x86/vm/atomic_linux_x86.hpp
判断处理器数量 > 0 时,执行lock指令,锁定总线,刷新数据到内存,使各处理器的缓存失效,保证目标值对其他线程立即可见
#define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: "
Intel手册对lock前缀指令保证:
- 确保对内存的读改写操作原子执行
- 禁止该指令与之前和之后的读或写指令重排序
- 把写缓冲区中的所有数据刷新到内存中
这三条足以同时满足volatile读和写内存的语义,所以说CAS同时满足了volatile的读写语义,这个语义作为CAS的充分条件,使得CAS原子操作在不加锁时能够保证变量的读写对其他线程立即可见。可以看到在多处理器系统上AtomicInteger的整个自增操作可能伴随着多次lock操作。do {} while() 循环可能非常耗时。CAS即是天使也是魔鬼。