热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

结合Hotspot源码谈谈:为什么CAS的比较和替换操作是原子性的?

本文前置知识:CAS算法原理和ABA问题之前提到,CAS的原理就是在将数据写入主内存的时候,将主内存的值与第一次读取到的值进行比较,如果两

本文前置知识: CAS算法原理和ABA问题

    之前提到,CAS的原理就是 在 将数据写入主内存的时候,将主内存的值与第一次读取到的值进行比较,如果两个值相同,则执行更新,否则,就继续循环。
     那么细心的人可能会思考,如果在 比较 -> 更新 这两个过程之间,有别的线程修改了主内存的数据,那么不是一样会出现并发问题吗?
    答案是,并不会出现并发问题。 原因是,CAS的比较和更新这两个操作是原子性的,中间不会被别的线程打断

那么本篇文章来具体探讨一下,为什么CAS的 比较和更新 这两个步骤是原子性的。

我们还以AtomicInteger为例,从之前的文章,我们知道该类对数据的操作,底层依赖了CAS算法。 现在我们再去看一下。

首先看一下AtomicInteger类的getAndIncrement()方法, 该方法可以实现并发下对数据的自增操作。

public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);
}

我们看到该方法中调用了UnSafe类的getAndAddInt()方法, 我们去看一下。

public final int getAndAddInt(Object var1, long var2, int var4) {int var5;do {var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));return var5;
}

该方法使用while循环的方式调用了compareAndSwapInt()方法,通过方法名,可以得知这个方法即是使用CAS的方式为变量进行赋值。

如果CAS在写入之前比较两个值不相等,那么就执行while循环,直到成功为止。这也就是我们所说的自旋锁了。

那么compareAndSwapInt()这个方法又是怎么实现的呢?

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

我们看到,UnSafe类中的compareAndSwapInt()方法是一个 native方法,调用的是底层由c 或 c++ 编写的代码。

到这里我们需要查看Hotspot源码。源码下载地址
在这里插入图片描述

unsafe.cpp:

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

cmpxchg = compare and exchange 这个指令是比较并交换指令,我们继续看调用链。

atomic_linux_x86.inline.hpp 93行

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

is_MP = Multi Processor

os.hpp is_MP()

static inline bool is_MP() {// During bootstrap if _processor_count is not yet initialized// we claim to be MP as that is safest. If any platform has a// stub generator that might be triggered in this phase and for// which being declared MP when in fact not, is a problem - then// the bootstrap routine for the stub generator needs to check// the processor count directly and leave the bootstrap routine// in place until called after initialization has ocurred.return (_processor_count != 1) || AssumeMP;}

atomic_linux_x86.inline.hpp

#define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: "

通过对Hotspot源码的调用链跟踪,最终得出CAS底层的实现方式:

lock cmpxchg 指令

这里的 cmpxchg指令 = cas修改变量值
lock指令的作用: 执行后面cmpxchg指令的时候锁定一个北桥信号 (不采用锁总线的方式),这个lock指令的效率要远高于JDK中实现的锁。


推荐阅读
  • 本文将详细介绍如何配置并整合MVP架构、Retrofit网络请求库、Dagger2依赖注入框架以及RxAndroid响应式编程库,构建高效、模块化的Android应用。 ... [详细]
  • 垂直泊车路径设计
    本文探讨了垂直泊车路径的设计原理与实现方法。垂直泊车是指汽车从特定位置出发,经过一系列横向和纵向移动,最终达到与车位垂直停放的状态。路径设计旨在确保泊车过程既高效又安全。 ... [详细]
  • 在Linux系统中使用EncFS实现文件夹加密
    为了保护个人隐私或敏感数据不被未经授权的访问,可以通过加密技术来增强安全性。本文介绍如何在Linux系统上使用EncFS工具创建和管理加密文件夹,以确保即使在系统登录状态下,特定文件夹中的数据也保持加密状态。 ... [详细]
  • 本文探讨了一个Web工程项目的需求,即允许用户随时添加定时任务,并通过Quartz框架实现这些任务的自动化调度。文章将介绍如何设计任务表以存储任务信息和执行周期,以及如何通过一个定期扫描机制自动识别并加载新任务到调度系统中。 ... [详细]
  • 本文介绍如何通过Java代码调用阿里云短信服务API来实现短信验证码的发送功能,包括必要的依赖添加和关键代码示例。 ... [详细]
  • 在AngularJS中,有时需要在表单内包含某些控件,但又不希望这些控件导致表单变为脏状态。例如,当用户对表单进行修改后,表单的$dirty属性将变为true,触发保存对话框。然而,对于一些导航或辅助功能控件,我们可能并不希望它们触发这种行为。 ... [详细]
  • 使用 ModelAttribute 实现页面数据自动填充
    本文介绍了如何利用 Spring MVC 中的 ModelAttribute 注解,在页面跳转后自动填充表单数据。主要探讨了两种实现方法及其背后的原理。 ... [详细]
  • 探讨多种方法来确定Java对象的实际类型,包括使用instanceof关键字、getClass()方法等。 ... [详细]
  • Java高级工程师学习路径及面试准备指南
    本文基于一位朋友的PDF面试经验整理,涵盖了Java高级工程师所需掌握的核心知识点,包括数据结构与算法、计算机网络、数据库、操作系统等多个方面,并提供了详细的参考资料和学习建议。 ... [详细]
  • Linux内核中的内存反碎片技术解析
    本文深入探讨了Linux内核中实现的内存反碎片技术,包括其历史发展、关键概念如虚拟可移动区域以及具体的内存碎片整理策略。旨在为开发者提供全面的技术理解。 ... [详细]
  • STM32代码编写STM32端不需要写关于连接MQTT服务器的代码,连接的工作交给ESP8266来做,STM32只需要通过串口接收和发送数据,间接的与服务器交互。串口三配置串口一已 ... [详细]
  • 本文详细探讨了 Android Service 组件中 onStartCommand 方法的四种不同返回值及其应用场景。Service 可以在后台执行长时间的操作,无需提供用户界面,支持通过启动和绑定两种方式创建。 ... [详细]
  • iOS如何实现手势
    这篇文章主要为大家展示了“iOS如何实现手势”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“iOS ... [详细]
  • 告别jQuery:Bootstrap5即将全面脱离jQuery依赖及其他前端热点
    本文精选了2019年2月「前端大全」平台上的15篇热门文章,涵盖技术分享与资源推荐。关注前端大全,获取更多前沿信息,提升您的前端技能。 ... [详细]
  • 本文总结了 #define 在 C/C++ 编程中的多种用途和技巧,包括定义常量、函数、宏以及条件编译等,并提供了详细的示例和注意事项。 ... [详细]
author-avatar
燕子yanzi068_476
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有