JAVA CAS底层实现

作者: 老荀 | 来源:发表于2020-04-07 11:38 被阅读0次

JAVA CAS

Java CAS底层都是调用了Unsafe类的compareAndSwap方法
都是native的方法

package sun.misc;
...
public final class Unsafe {
    ...
    public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
    public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
    ...
}

然后源码都是c或者c++实现了

// 源码路径 openjdk/hotspot/src/share/vm/prims/unsafe.cpp
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);
  oop res = oopDesc::atomic_compare_exchange_oop(x, addr, e, true);
  jboolean success  = (res == e);
  if (success)
    update_barrier_set((void*)addr, x);
  return success;
UNSAFE_END

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
  UnsafeWrapper("Unsafe_CompareAndSwapInt");
  oop p = JNIHandles::resolve(obj);
  jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
  return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x))
  UnsafeWrapper("Unsafe_CompareAndSwapLong");
  Handle p (THREAD, JNIHandles::resolve(obj));
  jlong* addr = (jlong*)(index_oop_from_field_offset_long(p(), offset));
  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;
  }
UNSAFE_END
#include "runtime/atomic.inline.hpp"
// 源码路径 hotspot/src/share/vm/runtime/atomic.cpp
jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value) {
  assert(sizeof(jbyte) == 1, "assumption.");
  uintptr_t dest_addr = (uintptr_t)dest;
  uintptr_t offset = dest_addr % sizeof(jint);
  volatile jint* dest_int = (volatile jint*)(dest_addr - offset);
  jint cur = *dest_int;
  jbyte* cur_as_bytes = (jbyte*)(&cur);
  jint new_val = cur;
  jbyte* new_val_as_bytes = (jbyte*)(&new_val);
  new_val_as_bytes[offset] = exchange_value;
  while (cur_as_bytes[offset] == compare_value) {
    jint res = cmpxchg(new_val, dest_int, cur);
    if (res == cur) break;
    cur = res;
    new_val = cur;
    new_val_as_bytes[offset] = exchange_value;
  }
  return cur_as_bytes[offset];
}

unsigned Atomic::cmpxchg(unsigned int exchange_value,
                         volatile unsigned int* dest, unsigned int compare_value) {
  assert(sizeof(unsigned int) == sizeof(jint), "more work to do");
  return (unsigned int)Atomic::cmpxchg((jint)exchange_value, (volatile jint*)dest,
                                       (jint)compare_value);
}
// 源码路径 hotspot/src/share/vm/runtime/atomic.inline.hpp
// 不同的操作系统不同架构,加载的文件就会不同
#ifndef SHARE_VM_RUNTIME_ATOMIC_INLINE_HPP
#define SHARE_VM_RUNTIME_ATOMIC_INLINE_HPP

#include "runtime/atomic.hpp"

// Linux
#ifdef TARGET_OS_ARCH_linux_x86
# include "atomic_linux_x86.inline.hpp"
#endif
#ifdef TARGET_OS_ARCH_linux_sparc
# include "atomic_linux_sparc.inline.hpp"
#endif
#ifdef TARGET_OS_ARCH_linux_zero
# include "atomic_linux_zero.inline.hpp"
#endif
#ifdef TARGET_OS_ARCH_linux_arm
# include "atomic_linux_arm.inline.hpp"
#endif
#ifdef TARGET_OS_ARCH_linux_ppc
# include "atomic_linux_ppc.inline.hpp"
#endif

// Solaris
#ifdef TARGET_OS_ARCH_solaris_x86
# include "atomic_solaris_x86.inline.hpp"
#endif
#ifdef TARGET_OS_ARCH_solaris_sparc
# include "atomic_solaris_sparc.inline.hpp"
#endif

// Windows
#ifdef TARGET_OS_ARCH_windows_x86
# include "atomic_windows_x86.inline.hpp"
#endif

// BSD
#ifdef TARGET_OS_ARCH_bsd_x86
# include "atomic_bsd_x86.inline.hpp"
#endif
#ifdef TARGET_OS_ARCH_bsd_zero
# include "atomic_bsd_zero.inline.hpp"
#endif

#endif // SHARE_VM_RUNTIME_ATOMIC_INLINE_HPP

我们这里就看看BSD和Linux的系统

// BSD
// 源码路径 hotspot/src/os_cpu/bsd_x86/vm/atomic_bsd_x86.inline.hpp
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;
}

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;
}
// Linux
// 源码路径 hotspot/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp
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;
}

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;
}

比较下来这两个不同操作系统的是一样的


image.png

既然都看了就顺便再看下windows,可以看到windows的大不一样了

// Windows
// 源码路径 hotspot/src/os_cpu/windows_x86/vm/atomic_windows_x86.inline.hpp
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
  }
}

上面的__asm__和__asm都是内联了汇编语言的写法
首先LOCK_IF_MP是定义在上面的宏用来判断当前运行环境是不是多核的,多核的就要加锁

// Linux, BSD
#define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: "
// Windows
标记处
#define LOCK_IF_MP(mp) __asm cmp mp, 0  \
// 如果 mp = 0,表明是线程运行在单核 CPU 环境下。此时 je 会跳转到 L0 
                       __asm je L0      \
// 0xF0 是 lock 前缀的机器码,多核的情况下会多执行emit这个指令
                       __asm _emit 0xF0 \
                       __asm L0:

至于为什么要用硬编码,参考了知乎的回答
如下:

  • 首先要明确是一个事实,就是编译器并不会随着cpu的更新而即时更新,这就有可能出现cpu支持了某种指令,但是编译器不支持,然而你还想用这个指令,此时就可以用_emit来硬编码
  • 编译器不支持__asm lock __asm L0:__asm cmpxchg dword ptr [edx], ecx这种直接把lock单独放在一行的语法, 所以可以用_emit 来达到通过编译的效果

内联汇编语言的语法

asm volatile("Instruction List"
 
: Output
 
: Input
 
: Clobber/Modify);

序号占位符就是前面描述的%0、%1、%2、%3、%4、%5、%6、%7、%8、%9

dword 由 4 字节长(32 位整数)的数字表示的数据
qword 由 8 字节长(32 位整数)的数字表示的数据
ptr 是pointer的简写,代表指针,当两个操作数的宽度不一样时,就要用到ptr

参考

https://www.cnblogs.com/noKing/p/9094983.html
https://blog.csdn.net/shakesky/article/details/6343395
https://www.zhihu.com/question/50878124/answer/123099923

相关文章

  • Java原子类中CAS的底层实现

    Java原子类中CAS的底层实现 - GoldArowana - 博客园 Java原子类中CAS的底层实现 从Ja...

  • JAVA CAS底层实现

    JAVA CAS Java CAS底层都是调用了Unsafe类的compareAndSwap方法都是native的...

  • Java CAS底层实现详解

    前言 这篇文章将深入介绍CAS在JDK具体的实现方式,填补Java源码系列(7) -- AtomicInteger...

  • Java CAS 底层代码实现

    CAS 什么是CAS 在计算机科学中,比较和交换(Conmpare And Swap)是用于实现多线程同步的原子指...

  • java面试题

    java并发面试问题: 1.聊聊你对CAS的理解: 并发包下的autoInteger的底层实现,当autointe...

  • CAS底层实现

    先从一段代码开始 其结果如下 原子了每次运行的结果都能达到预期,而int基本无法达到。查看incrementAnd...

  • 浅析CompareAndSet(CAS)

    CAS:Compare and Swap,比较并交换。 java.util.concurrent包中借助CAS实现...

  • [并发J.U.C]---简单理解Unsafe

    前言 由于并发包中很多数据结构的底层实现都是依赖于CAS,CAS也就是用Unsafe实现. CAS操作有3个操作数...

  • Java - CAS用法及说明

    CAS即compare and swap,表示比较并交换,在java中依赖Unsafe类来实现,常见的CAS实现有...

  • java如何实现原子操作CAS

    在Java中可以通过锁和循环CAS的方式来实现原子操作。 使用循环CAS实现原子操作 JVM中的CAS操作正是利用...

网友评论

    本文标题:JAVA CAS底层实现

    本文链接:https://www.haomeiwen.com/subject/pglgphtx.html