ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

Java CAS 原理分析,聊一聊MySQL数据库中的那些锁

2021-09-06 10:01:37  阅读:244  来源: 互联网

标签:Java CAS value jint int 聊一聊 cmpxchg edx



`public class AtomicInteger extends Number implements java.io.Serializable {



    // setup to use Unsafe.compareAndSwapInt for updates

    private static final Unsafe unsafe = Unsafe.getUnsafe();

    private static final long valueOffset;



    static {

        try {

            // 计算变量 value 在类对象中的偏移

            valueOffset = unsafe.objectFieldOffset

                (AtomicInteger.class.getDeclaredField("value"));

        } catch (Exception ex) { throw new Error(ex); }

    }



    private volatile int value;

    

    public final boolean compareAndSet(int expect, int update) {

        /*  * compareAndSet 实际上只是一个壳子,主要的逻辑封装在 Unsafe 的  * compareAndSwapInt 方法中  */

        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);

    }

    

    // ......

}



public final class Unsafe {

    // compareAndSwapInt 是 native 类型的方法,继续往下看

    public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);

    // ......

}`


`// unsafe.cpp

/*  * 这个看起来好像不像一个函数,不过不用担心,不是重点。UNSAFE_ENTRY 和 UNSAFE_END 都是宏,  * 在预编译期间会被替换成真正的代码。下面的 jboolean、jlong 和 jint 等一些类型也都是宏:  *  * jni.h  *     typedef unsigned char   jboolean;  *     typedef unsigned short  jchar;  *     typedef short           jshort;  *     typedef float           jfloat;  *     typedef double          jdouble;  *  * jni_md.h  *     typedef int jint;  *     #ifdef _LP64 /* 64-bit */

 *     typedef long jlong;

 *     #else

 *     typedef long long jlong;

 *     #endif

 *     typedef signed char jbyte;

 */

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

  // 根据偏移量,计算 value 的地址。这里的 offset 就是 AtomaicInteger 中的 valueOffset

  jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);

  // 调用 Atomic 中的函数 cmpxchg,该函数声明于 Atomic.hpp 中

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

UNSAFE_END



// atomic.cpp

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");

  /*  * 根据操作系统类型调用不同平台下的重载函数,这个在预编译期间编译器会决定调用哪个平台下的重载  * 函数。相关的预编译逻辑如下:  *  * atomic.inline.hpp:  *    #include "runtime/atomic.hpp"  *  *    // Linux  *    #ifdef TARGET_OS_ARCH_linux_x86  *    # include "atomic_linux_x86.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  *  * 接下来分析 atomic_windows_x86.inline.hpp 中的 cmpxchg 函数实现  */

  return (unsigned int)Atomic::cmpxchg((jint)exchange_value, (volatile jint*)dest,

                                       (jint)compare_value);

}`

上面的分析看起来比较多,不过主流程并不复杂。如果不纠结于代码细节,还是比较容易看懂的。接下来,我会分析 Windows 平台下的 Atomic::cmpxchg 函数。继续往下看吧。


`// atomic_windows_x86.inline.hpp

#define LOCK_IF_MP(mp) __asm cmp mp, 0 \

 __asm je L0 \

 __asm _emit 0xF0 \

 __asm L0:

              

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

  }

}`

上面的代码由 LOCK_IF_MP 预编译标识符和 cmpxchg 函数组成。为了看到更清楚一些,我们将 cmpxchg 函数中的 LOCK_IF_MP 替换为实际内容。如下:


`inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {

  // 判断是否是多核 CPU

  int mp = os::is_MP();

  __asm {

    // 将参数值放入寄存器中

    mov edx, dest    // 注意: dest 是指针类型,这里是把内存地址存入 edx 寄存器中

    mov ecx, exchange_value

    mov eax, compare_value

    

    // LOCK_IF_MP

    cmp mp, 0

    /*  * 如果 mp = 0,表明是线程运行在单核 CPU 环境下。此时 je 会跳转到 L0 标记处,  * 也就是越过 _emit 0xF0 指令,直接执行 cmpxchg 指令。也就是不在下面的 cmpxchg 指令  * 前加 lock 前缀。  */

    je L0

    /*  * 0xF0 是 lock 前缀的机器码,这里没有使用 lock,而是直接使用了机器码的形式。至于这样做的  * 原因可以参考知乎的一个回答:  *     https://www.zhihu.com/question/50878124/answer/123099923  */ 

    _emit 0xF0

L0:

    /*  * 比较并交换。简单解释一下下面这条指令,熟悉汇编的朋友可以略过下面的解释:  *   cmpxchg: 即“比较并交换”指令  *   dword: 全称是 double word,在 x86/x64 体系中,一个  *          word = 2 byte,dword = 4 byte = 32 bit  *   ptr: 全称是 pointer,与前面的 dword 连起来使用,表明访问的内存单元是一个双字单元  *   [edx]: [...] 表示一个内存单元,edx 是寄存器,dest 指针值存放在 edx 中。  *          那么 [edx] 表示内存地址为 dest 的内存单元  *  * 这一条指令的意思就是,将 eax 寄存器中的值(compare_value)与 [edx] 双字内存单元中的值  * 进行对比,如果相同,则将 ecx 寄存器中的值(exchange_value)存入 [edx] 内存单元中。  */

    cmpxchg dword ptr [edx], ecx

  }

}`

到这里 CAS 的实现过程就讲完了,CAS 的实现离不开处理器的支持。以上这么多代码,其实核心代码就是一条带lock 前缀的 cmpxchg 指令,即lock cmpxchg dword ptr [edx], ecx

4.ABA 问题


谈到 CAS,基本上都要谈一下 CAS 的 ABA 问题。CAS 由三个步骤组成,分别是“读取->比较->写回”。考虑这样一种情况,线程1和线程2同时执行 CAS 逻辑,两个线程的执行顺序如下:

  1. 时刻1:线程1执行读取操作,获取原值 A,然后线程被切换走

  2. 时刻2:线程2执行完成 CAS 操作将原值由 A 修改为 B

  3. 时刻3:线程2再次执行 CAS 操作,并将原值由 B 修改为 A

  4. 时刻4:线程1恢复运行,将比较值(compareValue)与原值(oldValue)进行比较,发现两个值相等。

最后

对于很多Java工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

整理的这些资料希望对Java开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

再分享一波我的Java面试真题+视频学习详解+技能进阶书籍

CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】

升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。**

整理的这些资料希望对Java开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

再分享一波我的Java面试真题+视频学习详解+技能进阶书籍

CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】

美团二面惜败,我的凉经复盘(附学习笔记+面试整理+进阶书籍)

标签:Java,CAS,value,jint,int,聊一聊,cmpxchg,edx
来源: https://blog.csdn.net/m0_60634964/article/details/120127502

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有