首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CAS(compareAndSwap)原理

CAS(compareAndSwap)原理

作者头像
爬蜥
发布2019-07-09 19:37:26
1.5K0
发布2019-07-09 19:37:26
举报

unsafe中对应拥有三个方法 compareAndSwapObject ,compareAndSwapIntcompareAndSwapLong ,他们都被标记为native

compareAndSwapObject

它的核心实现为

oop res = oopDesc::atomic_compare_exchange_oop(x, addr, e);

实现核心如下

inline oop oopDesc::atomic_compare_exchange_oop(oop exchange_value,
                                                volatile HeapWord *dest,
                                                oop compare_value) {
  if (UseCompressedOops) {
    narrowOop val = encode_heap_oop(exchange_value);
    narrowOop cmp = encode_heap_oop(compare_value);

    narrowOop old = (narrowOop) Atomic::cmpxchg(val, (narrowOop*)dest, cmp);
    return decode_heap_oop(old);
  } else {
    return (oop)Atomic::cmpxchg_ptr(exchange_value, (oop*)dest, compare_value);
  }
}

UseCompressedOops: 32位平台运行的程序在64位上会占用更大的长度,可以使用 -XX:+UserCompressedOops压缩指针,达到节约内存的目的。

compareAndSwapInt

核心代码如下

return (jint)(Atomic::cmpxchg(x, addr, e)) == e;

compareAndSwapLong

核心代码如下

if (VM_Version::supports_cx8())
  return (jlong)(Atomic::cmpxchg(x, addr, e)) == e;
else {
  jboolean success = false;
  ObjectLocker ol(p, THREAD);
  if (*addr == e) { *addr = x; success = true; }
  return success;
}

supports_cx8:判断硬件是不是支持8-byte compare-exchange , x86架构中通过cpuid指令来获取是否试支持,CMPXCHG8指令 ;SPARC架构也是看 (_features & v9_instructions_m)指令的支持情况

Atomic::cmpxchg

无论是那个调用,最终都归结到了Atomic上,Atomic.hpp中函数声明如下

//比较当前的值和目的地址的值,如果比较成功,就把目的地址的值更改为exchange_value,并返回原来存的值
static jbyte    cmpxchg    (jbyte    exchange_value, volatile jbyte*    dest, jbyte    compare_value);
static jint     cmpxchg    (jint     exchange_value, volatile jint*     dest, jint     compare_value);
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);

从Atomic.cpp可以看到在不同的操作系统中有不同的实现 在 windows_x86中,一种实现如下

inline jint     Atomic::cmpxchg    (jint     exchange_value, volatile jint*     dest, jint     compare_value) {
  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
  }
}

linux_x86中,实现如下

inline jint     Atomic::cmpxchg    (jint     exchange_value, volatile jint*     dest, jint     compare_value) {
  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;
}

可以看到最终都是使用操作系统对应的指令来完成

都在哪儿用了

可以看到Atomic的实现就是用的CAS,比如AtomicIntegerincrementAndGet

public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}

这种一直循环的操作也称作自旋

CAS的缺点

  • 如果一直没有成功,则一直循环,给CPU带来很大的开销。
  • 只能是一个变量
  • ABA问题。一个变量取值为A,恰巧另一个线程将它换成了B然后又换回来了,这个时候再读取还是A,实际上是改变了值。java自身提供了AtomicStampedReference来解决这个问题,原理是添加一个额外的版本来做判断

源码来自jdk1.7

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年12月02日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • compareAndSwapObject
  • compareAndSwapInt
  • compareAndSwapLong
  • Atomic::cmpxchg
  • 都在哪儿用了
  • CAS的缺点
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档