【JavaEE初阶 — 多线程】内存可见性问题 volatile

   c96f743646e841f8bb30b2d242197f2f.gif

ddb5ae16fc92401ea95b48766cb03d96.jpeg692a78aa0ec843629a817408c97a8b84.gif

  1. 内存可见性问题  


   内存可见性的概念  


  什么是内存可见性问题呢?  

  • 当一个线程对共享变量进行了修改,那么另外的线程都是立即可以看到修改后的最新值。
  • 在Java中,可以借助 synchronized、volatile 以及各种Lock 实现可见性。
  • 如果我们将变量声明为 volatile,这就指示JVM,这个变量是共享且不稳定的,每次使用它都到主存中进行读取,不能优化。

   编译器优化   


造成内存可见性问题的原因,是因为编译器优化的机制而造成的,我们先介绍一下什么是编译器优化。


   背景   


为什么要有编译器优化这样的机制呢?由于程序员的水平参差不齐,研究JDK 的大佬们,就希望通过让 编译器 & JVM 对程序员写的代码,自动的进行优化;


    优点    


  • 对于程序员原有的代码,编译器/JVM会在原有逻辑不变的前提下,对代码进行调整,使程序效率更高;这样的操作,就是编译器优化。
  • 也就是说,我们写的代码,编译器会在原有逻辑上,帮我们调整代码,通过编译器优化,可保证原有逻辑不变的前提下,对代码进行修改,使得执行效率得到提高。

    缺点   


  • 编辑器在编译代码的时候,其实它并没有执行代码,它只是根据这个编译的一个静态的代码,来分析这个程序应该怎么去进行调整;所以编译器 “保证原因逻辑不变,再进行优化 ”,这样的 “保证” 并非是能够 100% 生效的;
  • 尤其是很多线线程的程序中,因为并发编程随机调度的特点,使得执行多线程程序的过程中,可能会出现诸多变数,这些变数可能会导致编译器出现判断失误;
  • 因此,编译器在针对不同的程序,能够做出的判断是有限,并且容易失误的。所以在经过编译器优化后的代码逻辑,与优化前的逻辑,可能会出现偏差。

   案例描述  


 内存可见性问题是造成线程安全问题的原因之一,我们通过下面的代码来感受一下,内存可见性是如何造成线程安全问题的:

   代码逻辑:


  • 我们定义一个成员变量 flag ,用来作为 t1 线程 run() 方法中的循环终止条件;
  • t2 线程用来修改 flag 的值;
  • 这样的操作,相当于一个线程进行读取,另一个线程进行修改

   原子性问题和可见性问题代码演示的区别   


  • 这里演示因为内存可见性,造成线程安全问题,是令一个线程进行读取另一个线程进行修改
  • 演示因为操作非原子性,而造成线程安全问题,是令两个线程同时修改同一个变量,所以两个线程都是在进行修改操作;

   预期效果:


  • 只要我们通过 t2 ,输入给 flag 的数字是一个非零的值,就会使得我们 t1 线程的循环能够结束;

  程序运行结果:

但是当我真正输入一个非零值的时候,回车,发现 t1 线程并没有结束循环,打印结束日志


   通过 Jconsole 观察 t1 线程的状态   

  • 因为 t2 线程只有一次输入修改 flag 的操作,已经终止;  
  • 观察到 t1 的线程状态是Runnable,正在持续执行循环;
  • 在 t2 线程输入非零值,能让 t1 线程循环结束,进而 t1 终止,这是我们预期结果;
  • 但是实际执行结果却并非如此;
  • 一个线程读取,一个线程修改,t2(修改线程) 修改的值,并没有被 t1(读取线程)读到,这就是因为编译器优化而造成的"内存可见性问题”;

  分析出现内存可见性问题的原因  

对于上述代码中,t1 线程的循环判断条件 flag== 0,对其进行细分,会分出两个指令,分别是比较指令(==)和读取指令(读flag);

程序会先执行读取指令 load ,只有把 flag 这个变量在内存中的值,读取到寄存器中,才会执行比较指令 cmp;

而因为 load,cmp 两步指令是在循环中完成的,while 循环如果没有休眠限制,会在短时间内循环多次,从而重复执行多次 load -> cmp 这样的指令。

但是,load (读内存操作)和 cmp (纯CPU寄存器操作)两步指令的开销是非常大的;

load 的时间开销是 cmp 的几千倍,因为虽然读内存数据比读硬盘数据要快很多,但是如果是拿CPU寄存器和内存比,那就是寄存器快很多;

因此,在 t1 创建好后,run() 方法执行的时间,几乎都在load,cmp 的时间开销是可以忽略不计的;

所以在执行的过程中,JVM就能感知到,load 反复执行的结果是一样的;哪怕我们通过 t2 的 scanner 输入 flag 的时间只有不到 1s,但是站在计算机的角度,这 1s 可以说是沧海桑田

因此,程序在执行的过程中,JVM 会感受到程序一直在反复读内存的值;

为了减小时间开销, t1 线程的读操作,会被编译器优化成:从读内存的值,到读CPU寄存器(t1 线程的工作内存)的值;

后续再执行 load 指令,就不会再重新读内存,而是直接从寄存器(工作内存)中读取,从而大大减小开销,并且提高了效率;

于是,等到很多秒后,用户真正输入新的值,真正修改 flag 的值,此时 t1 线程就感知不到了

(编译器优化,使得 t1 线程的读操作,不是读取内存)


   2. JMM模型文档    

JMM(Java 内存模型)详解


   3. volatile   


volatile 能保证内存可见性


内存可见性就是保证, 每次去读取的时候,  读取到的值都是最新的值(内存中的值),而不是之前缓存在寄存器中的值;volatile 修饰的变量,能够保证"内存可见性";


代码在写入 volatile 修饰的变量的时候

  • 改变线程工作内存中volatile变量副本的值
  • 将改变后的副本的值从工作内存刷新到主内存

代码在读取 volatile 修饰的变量的时候,

  • 从主内存中读取 volatile 变量的最新值到线程的工作内存中
  • 从工作内存中读取 volatile 变量的副本

前面我们讨论内存可见性时说了,直接访问工作内存(实际是CPU 的寄存器或者 CPU 的缓存),速度
非常快,但是可能出现数据不一致的情况;


加上 volatile,强制读写内存,速度是慢了,但是数据变的更准确了:


  c96f743646e841f8bb30b2d242197f2f.gif

692a78aa0ec843629a817408c97a8b84.gif

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

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

相关文章

通用特效Shader

一、通用特效Shader介绍 1.1 什么是通用特效材质 Unity支持SRP Batcher后,使用UberShader的优势非常明显。所谓,UberShader,即一个超级Shader,覆盖一类功能,而不是多个分散的小Shader,比如一个通用特效Sh…

spark-本地模式的配置和简单使用

python环境的安装 在虚拟机中,只能安装一个python的版本,若想要安装别的版本,则需要卸载之前的版本——解决方式,安装Anaconda 通过百度网盘分享的文件:Anaconda3-2021.05-Linux-x86_64.sh 链接:https://…

分享三个python爬虫案例

一、爬取豆瓣电影排行榜Top250存储到Excel文件 近年来,Python在数据爬取和处理方面的应用越来越广泛。本文将介绍一个基于Python的爬虫程序,用于抓取豆瓣电影Top250的相关信息,并将其保存为Excel文件。 获取网页数据的函数,包括以…

PyQt5 详细安装与配置教程及使用

文章目录 Part1:安装 PyQt5Part2:配置 PyQt5 的依赖工具 QtDesigner 和 PyUICPart3:使用QtDesigner设计界面Part4:使用PyUIC将设计好的界面转换为.py文件Part5:通过代码显示ui界面 Part1:安装 PyQt5 需要安…

ssm079基于SSM框架云趣科技客户管理系统+jsp(论文+源码)_kaic

毕 业 设 计(论 文) 题目:客户管理系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本客户管理系统就是在这…

C语言 | Leetcode C语言题解之第556题下一个更大元素III

题目&#xff1a; 题解&#xff1a; int nextGreaterElement(int n){int x n, cnt 1;for (; x > 10 && x / 10 % 10 > x % 10; x / 10) {cnt;}x / 10;if (x 0) {return -1;}int targetDigit x % 10;int x2 n, cnt2 0;for (; x2 % 10 < targetDigit; x2…

华为大变革?仓颉编程语言会代替ArkTS吗?

在华为鸿蒙生态系统中&#xff0c;编程语言的选择一直是开发者关注的焦点。近期&#xff0c;华为推出了自研的通用编程语言——仓颉编程语言&#xff0c;这引发了关于仓颉是否会取代ArkTS的讨论。本文将从多个角度分析这两种语言的特点、应用场景及未来趋势&#xff0c;探讨仓颉…

Linux:基本开发工具

一&#xff1a;编辑器vim 1.1vim的基本概念 vim其实有多重模式&#xff0c;这里我们主要了解vim的三种模式&#xff0c;分别是命令模式&#xff08;command mode&#xff09;,插入模式(Insert mode)和底行模式(lst line mode) 正常/普通/命令模式(Normal mode) …

第14张 GROUP BY 分组

一、分组功能介绍 使用group by关键字通过某个字段进行分组&#xff0c;对分完组的数据分别 “SELECT 聚合函数”查询结果。 1.1 语法 SELECT column, group_function(column) FROM table [WHERE condition] [GROUP BY group_by_expression] [ORDER BY column]; 明确&#…

TVM计算图分割--BYOC框架

文章目录 BYOC架构算子标注单算子标注复合算子标注Cost-based PartitionCodegenCodegen for C代码生成流程概览代码生成工程实现实现CodegenC实现CSourceCodegenCodegen for JSON实现JsonCodegenRuntimeJSONRuntime参考随着后端设备数量激增,为达到较高的效果在这些设备上,对…

计算机毕业设计Python+卷积神经网络股票预测系统 股票推荐系统 股票可视化 股票数据分析 量化交易系统 股票爬虫 股票K线图 大数据毕业设计 AI

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

qt QShortcut详解

1、概述 QShortcut是Qt框架中的一个类&#xff0c;它提供了一种创建键盘快捷键的方式。通过QShortcut&#xff0c;开发者可以将特定的键盘组合&#xff08;如CtrlC、AltF4等&#xff09;与应用程序中的动作&#xff08;如复制、关闭窗口等&#xff09;关联起来。当用户在应用程…

C++OJ_二叉树的层序遍历

✨✨ 欢迎大家来到小伞的大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C_OJ 小伞的主页&#xff1a;xiaosan_blog 二叉树的层序遍历 102. 二叉树的层序遍历 - 力扣&#xff08;LeetCode&#xff0…

The Rank-then-Encipher Approach

原始观点 Format-Preserving Encryption 4 The Rank-then-Encipher Approach 引用1 Hybrid diffusion-based visual image encryption for secure cloud storage 2.2 Sum-preserving encryption Bellare introduced the concept of format-preserving encryption (FPE)…

DolphinDB 与南方科技大学联合授课啦!

11月1日&#xff0c;南方科技大学商学院和 DolphinDB 联合举办了高校课程讲座。讲座由南方科技大学商学院高级研究学者冯鹏举主持&#xff0c;DolphinDB 创始人兼 CEO 周小华博士、某百亿私募数据平台架构师潜蛟老师进行精彩演讲。 Part 1 : 大数据时代下数据库架构革新与生态…

IDM扩展添加到Edge浏览器

IDM扩展添加到Edge浏览器 一般情况下&#xff0c;当安装IDM软件后&#xff0c;该软件将会自动将IDM Integration Module浏览器扩展安装到Edge浏览器上&#xff0c;但在某些情况下&#xff0c;需要我们手动安装&#xff0c;以下为手动安装步骤 手动安装IDM扩展到Edge浏览器 打…

403 Request Entity Too Lager(请求体太大啦)

昨天收到 QA 的生产报障&#xff0c;说是测试环境的附件上传功能报了 403 的错误&#xff0c;错误信息&#xff1a;403 Request Entity Too Lager。我尝试复现问题&#xff0c;发现传个几兆的文件都费劲啊&#xff0c;一传一个失败。不用说&#xff0c;项目用到 ng 代理&#x…

HARCT 2025 新增分论坛2:机器人系统智能控制

会议名称&#xff1a;机电液一体化与先进机器人控制技术国际会议 会议简称&#xff1a;HARCT 2025 大会时间&#xff1a;2025年1月3日-6日 大会地点&#xff1a;中国桂林 主办单位&#xff1a;桂林航天工业学院、广西大学、桂林电子科技大学、桂林理工大学 协办单位&#…

网络世界中的侦察兵----ICMP

前言 学习了IP协议后&#xff0c;都知道IP协议本身是不提供可靠性保障的&#xff0c;那么数据包在这么复杂的互联网环境中传输&#xff0c;总会遇到问题&#xff0c;如果遇到问题后&#xff0c;被丢弃、无回应&#xff0c;可能作为工程师的我们来说都不知道发生了什么事&#…

从0开始学习机器学习--Day21--算法的评估标准

准确率和召回率(precision and recall) 在上一章我们提到了在每次运行算法时通过返回一个实数值来判断算法的好坏&#xff0c;但是我们该如何构建这个实数的计算公式呢&#xff0c;毕竟这关乎于我们对算法的判断&#xff0c;不能过于夸大或贬低。有一个典型的会被影响的很大例…