JMM内存模型 volatile关键字解析

前言

对于多线程等等的各种操作,相比各位都了然于胸,现在我们来介绍一下更底层一点点的JMM内存模型,其实也是一个很简单的理想的内存模型

注意与JVM的内存模型区分

多线程内存模型主要是基于CPU缓存搭建起来的

这里就区分工作内存和主内存了

我们线程操作的其实是主内存的一个副本,多线程每个线程操作结束了之后需要刷回主内存

volatile关键字 

我们知道volatile主要的两个功能就是

1.保证内存中的变量可见性

2.禁止指令重排序

下面我们来介绍一些关于volatile关键字以及高并发的内容

我们知道,如果两个线程同时操作一个变量,我们这里就称之为线程a和线程b,a线程假设操作了主内存的一个变量,另一个线程能感知到吗?

答案是不能,因为两者始终操作的是其工作内存中的变量副本而已.

如果这里我给共享的变量加入一个volatile关键字修饰,这里就b线程就能感知到工作内存中的变量变化了,这是为什么呢?

请听我慢慢解释

首先我们需要先了解一下经典的原子操作

然后我们来谈谈另一个线程是怎么感知到的

说到这里就不得不提我们的MESI(缓存一致性)协议了

这里就是线程a一修改主内存中的变量,其实这个修改是通过总线传输到主内存的

这里线程b就是通过总线嗅探机制,一直在监听总线,当他发现这里总线中有我的属性被修改了

这里我们的b线程对应的变量的属性就直接设置为I(无效),当下次线程b需要使用这个变量的时候哦,他就只能取主内存里面再去刷入工作内存了

那么我们的硬件协议又是怎么让缓存一致性协议生效的呢?

其本质就是在其底层的汇编代码前面加上了lock前缀

注意这里是修改完直接就同步回主内存,主打一个即时性

关于这里的指令重排序,我们也来谈一谈

为什么指令重排序

这里的指令重排序主要是为了加快程序性能而产生的

遵循 happens before 和 as is serial 原则

本质上就是在下面一条语句依赖于上面一条语句的时候

不会执行指令重排序,不影响依赖关系就随便排序

注:java 在执行代码之前会看看语法树前后有没有相互依赖

下面我展示部分原则

懒汉模式出现的对象半初始化问题

我们知道懒汉模式这里会使用双重校验锁

我拿出了其两行字节码指令

假设这里的putstatic在init之前

这里刚刚putstatic之后,cpu就调度到另一个线程了

这里判断已经不是空了,直接拿来使用,就会发生意想不到的问题

假设我这里a开了一个账户充6000块,然后直接去消费了

结果消费的时候发现账户的前不翼而飞了,所以这里的指令重排序问题是一个大的问题

常见的几种内存屏障

我们都知道volatile关键字是底层实现其实是一些内存屏障来实现的

我这里贴出几种常见的内存屏障

然后我们可以查看一下Java具体是怎么实现的

我们以 openjdk8 根路径 jdk\src\hotspot\share\interpreter\zero 路径下的 bytecodeInterpreter.cpp 文件中,处理 putstatic 指令的代码:

先进行判断是否有volatile修饰的实例

然后判断是对于不同的修饰类型进行操作

CASE(_putstatic):
    {
          // .... 省略若干行 
          // Now store the result 现在要开始存储结果了
          // ConstantPoolCacheEntry* cache;     -- cache是常量池缓存实例
          // cache->is_volatile()               -- 判断是否有volatile访问标志修饰
          int field_offset = cache->f2_as_index();
          // ****重点判断逻辑**** 
          if (cache->is_volatile()) { 
            // volatile变量的赋值逻辑
            if (tos_type == itos) {
              obj->release_int_field_put(field_offset, STACK_INT(-1));
            } else if (tos_type == atos) {// 对象类型赋值
              VERIFY_OOP(STACK_OBJECT(-1));
              obj->release_obj_field_put(field_offset, STACK_OBJECT(-1));
              OrderAccess::release_store(&BYTE_MAP_BASE[(uintptr_t)obj >> CardTableModRefBS::card_shift], 0);
            } else if (tos_type == btos) {// byte类型赋值
              obj->release_byte_field_put(field_offset, STACK_INT(-1));
            } else if (tos_type == ltos) {// long类型赋值
              obj->release_long_field_put(field_offset, STACK_LONG(-1));
            } else if (tos_type == ctos) {// char类型赋值
              obj->release_char_field_put(field_offset, STACK_INT(-1));
            } else if (tos_type == stos) {// short类型赋值
              obj->release_short_field_put(field_offset, STACK_INT(-1));
            } else if (tos_type == ftos) {// float类型赋值
              obj->release_float_field_put(field_offset, STACK_FLOAT(-1));
            } else {// double类型赋值
              obj->release_double_field_put(field_offset, STACK_DOUBLE(-1));
            }
            // *** 写完值后的storeload屏障 ***
            OrderAccess::storeload();
          } else {
            // 非volatile变量的赋值逻辑
          }       
  }

我们看到这里判断完的的storeload

然后我们介绍一下这个fence函数

这里先判断使用的显卡还是其他显卡

其实没有什么区别,主要是amd使用rsp,其他显卡使用的是esp,使用的寄存器不同

inline void OrderAccess::fence() {
   // 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();
}

这个__asm__就是表示告诉编译器在这里插入汇编代码

volatile就是告诉编译器我这里插入的汇编代码原原本本的给我执行,不许重排序

我们发现这些名字前面都有一个lock就是会将这块内存区域的缓存锁定并写回到主内存中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/514804.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【WEEK6】 【DAY3】MySQL函数【中文版】

2024.4.3 Wednesday 目录 5.MySQL函数5.1.常用函数5.1.1.数据函数5.1.2.字符串函数5.1.2.1.CHAR_LENGTH(str)计算字符串str长度5.1.2.2.CONCAT(str1,str2,...)拼接字符串str1 str2 ...5.1.2.3.INSERT(str,pos,len,newstr)把原文str第pos位开始长度为len的字符串替换成newstr5.…

Springboot传参要求

传参的参数名称必须与Set方法的参数名字相同 ,不然会报错。

PAC的启用与构建

PAC如何启用?构建PAC的编译选项控制?本博客探讨这几个问题。

【局部路径规划算法】—— DWA动态窗口法(c++实现))

参考资料: (1)机器人局部避障的动态窗口法(dynamic window approach) (2)机器人局部避障的动态窗口法 (3)局部规划算法:DWA算法原理 (4)SLAM学习:…

Android Monkey自动化测试

monkey一般用于压力测试,用户模拟用户事件 monkey 基本用法 adb shell monkey [参数] [随机事件数]monkey常用命令 -v:用于指定反馈信息级别,总共分三个等级-v -v -vadb shell mokey -v -v -v 100-s:用于指定伪随机数生成器的种…

安卓MT管理器v2.15.1

软件介绍 MT管理器是一款强大的文件管理工具和APK逆向修改神器。如果你喜欢它的双窗口操作风格,可以单纯地把它当成文件管理器使用。如果你对修改APK有深厚的兴趣,那么你可以用它做许许多多的事,例如汉化应用、替换资源、修改布局、修改逻辑…

相交链表 - LeetCode 热题 22

大家好!我是曾续缘💤 今天是《LeetCode 热题 100》系列 发车第 22 天 链表第 1 题 ❤️点赞 👍 收藏 ⭐再看,养成习惯 相交链表 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果…

CVE-2021-30517:Type confusion bug in LoadSuperIC

前言 这个漏洞是一个比较老的洞,之所以分析这个漏洞,只要是想再学习一下 ICs 相关的知识。并该漏洞的利用是利用与 String/Function 之间的混淆,比较有意思。 环境搭建 sudo apt install python git checkout 7d5e5f6c62c3f38acee12dc4114…

【蓝桥杯第十四届省赛B】(部分详解)

【01串的熵】 https://www.lanqiao.cn/problems/3498/learning/?subject_code1&group_code4&match_num14&match_flow1&origincup #include <iostream> #include<cmath> using namespace std; int main() {double n23333333;double sum0;for(int…

代码学习记录35

随想录日记part35 t i m e &#xff1a; time&#xff1a; time&#xff1a; 2024.04.03 主要内容&#xff1a;今天开始要学习动态规划的相关知识了&#xff0c;今天的内容主要涉及三个方面&#xff1a; 最后一块石头的重量 II &#xff1b;目标和 &#xff1b;一和零 。 1049…

Linux基础篇:文件系统介绍——根目录下文件夹含义与作用介绍

Linux文件系统介绍——文件夹含义与作用 Linux文件系统是一个组织和管理文件的层次结构。它包括了目录、子目录和文件&#xff0c;这些都是按照一定的规则和标准进行组织的。以下是Linux文件系统的一些关键组成部分&#xff1a; 1./bin&#xff1a; 该目录包含了系统启动和运…

抽象类与接口(3)(接口部分)

❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&…

TiDB单机版安装和连接访问

TiDB单机版安装和连接访问 1、下载 $wget http://download.pingcap.org/tidb-latest-linux-amd64.tar.gz 2、解压缩 $tar -zxvf tidb-latest-linux-amd64.tar.gz 3、启动TiDB 启动PD $./bin/pd-server --data-dirpd --log-filepd.log 启动tikv $./bin/tikv-server --pd…

基于深度学习的扑克牌识别软件(网页版+YOLOv8_v7_v6_v5代码+训练数据集)

摘要&#xff1a;本文深入研究了基于YOLOv8/v7/v6/v5的扑克牌识别软件&#xff0c;核心采用YOLOv8并整合了YOLOv7、YOLOv6、YOLOv5算法&#xff0c;进行性能指标对比&#xff1b;详述了国内外研究现状、数据集处理、算法原理、模型构建与训练代码&#xff0c;及基于Streamlit的…

NASA数据集——1983 ——2016 年期间北美森林地点的野外地块特征数据、衍生的地上和地下燃烧碳以及获取的火灾气象指数(FWI)

文件修订日期&#xff1a;2022-05-04 数据集版本: 1 简介 该数据集综合了 1983 年至 2016 年期间美国阿拉斯加、西北地区和加拿大萨斯喀彻温省被烧毁的北方森林地点的野外地块特征数据、衍生的地上和地下燃烧碳以及获取的火灾气象指数&#xff08;FWI&#xff09;系统组件。…

基于 Quartz.NET 可视化任务调度平台 QuartzUI

一、简介 QuartzUI 是基于 Quartz.NET3.0 的定时任务 Web 可视化管理&#xff0c;Docker 打包开箱即用、内置 SQLite 持久化、语言无关、业务代码零污染、支持 RESTful 风格接口、傻瓜式配置、异常请求邮件通知等。 二、部署 QuartzUI 从 2022 年到现在没有提交记录&#xf…

计算机网络:局域网的数据链路层

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

SQLyog连接数据库8.0版本解析错误问题解决方案

问题描述&#xff1a; 解决方案&#xff1a; alter userrootlocalhostidentified with mysql_native_password by 密码; 再次连接就可以了。

Autodesk AutoCAD 2025 (macOS, Windows) - 自动计算机辅助设计软件

Autodesk AutoCAD 2025 (macOS, Windows) - 自动计算机辅助设计软件 AutoCAD 2024 开始原生支持 Apple Silicon&#xff0c;性能提升至 2 倍 请访问原文链接&#xff1a;https://sysin.org/blog/autodesk-autocad/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处…

Express框架搭建项目 node.js

文章目录 引言Express框架介绍express安装环境准备写一个简单的项目展示 文章总结 引言 Express是一个基于Node.js平台的轻量级Web应用框架&#xff0c;它提供了简洁的API和丰富的功能&#xff0c;使得开发者能够快速地构建Web服务器和API。本文将带领大家从零开始&#xff0c…