HotSpot CAS原理

在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

  1. o: AtomicInteger类
  2. offset: value变量的在内存中的相对偏移
  3. 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

  1. offset : value偏移
  2. e:getIntVolatile读到的最新值,也即期望值
  3. 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前缀指令保证:

  1. 确保对内存的读改写操作原子执行
  2. 禁止该指令与之前和之后的读或写指令重排序
  3. 把写缓冲区中的所有数据刷新到内存中

这三条足以同时满足volatile读和写内存的语义,所以说CAS同时满足了volatile的读写语义,这个语义作为CAS的充分条件,使得CAS原子操作在不加锁时能够保证变量的读写对其他线程立即可见。可以看到在多处理器系统上AtomicInteger的整个自增操作可能伴随着多次lock操作。do {} while() 循环可能非常耗时。CAS即是天使也是魔鬼。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值