【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

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

3195. 有趣的数-13年12月CCF计算机软件能力认证(组合数)

题目 思路 统计方案的时候先去分类,先放01,然后在考虑23对于第k类, 对于01的选择 对于所有的分类:本题我觉得要考虑的几个点就是:状态分类得到数学公式组合数的计算防越界处理 代码 计算组合数的代码模板&#xff1…

goframe开发一个企业网站 开发环境DOCKER 搭建16

Docker开发环境搭建的优势 环境一致性 消除"在我机器上能运行"的问题保证开发、测试、生产环境的一致性新成员可以快速搭建完整开发环境 快速部署 一键启动所有依赖服务配置文件版本控制环境迁移方便快捷 资源隔离 避免不同项目依赖冲突系统资源可控便于多版本…

C++builder中的人工智能(20):如何在C++中开发一个简单的Hopfield网络

在AI技术的发展历史中,模式识别模型是最伟大的AI技术之一,尤其是从像素图像中读取文本。其中一个是Hopfield网络(或称为Ising模型的神经网络或Ising–Lenz–Little模型),这是一种递归神经网络形式,由John J…

【Leetcode 中等】34. 在排序数组中查找元素的第一个和最后一个位置

原题链接 Leetcode 34. 在排序数组中查找元素的第一个和最后一个位置 题目 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。 你必…

金融学期末速成笔记

【拯救者】金融学速成(基础习题) 重点: 市场经济是发达的商品经济。在市场经济条件下,市场机制作为资源配置方式,发挥基础性作用。 除具有商品经济的一般特征外,与商品经济相比,市场经济还具有一些新的特征…

云计算复习文档

云计算复习文档 一 云计算概述 名词: 云计算 1.0 : 面向数据中心管理员的IT基础设施资源虚拟化阶段 通过计算虚拟化技术将企业IT应用与底层的基础设施彻底分离、解耦 将多个企业IT应用实例及运行环境复用在相同的物理服务器上,并通过虚…

【Docker容器化技术】docker安装与配置、常用命令、容器数据卷、应用部署实战、Dockerfile、服务编排docker-compose、私有仓库

文章目录 一、Docker的安装与配置1、docker概述2、安装docker3、docker架构4、配置镜像加速器 二、Docker命令1、服务相关命令2、镜像相关命令3、容器相关命令 三、Docker容器数据卷1、数据卷概念及作用2、配置数据卷3、配置数据卷容器 四、Docker应用部署实战1、部署MySQL2、部…

window11安装elasticsearch+Kibana

1、下载elasticsearch与elasticsearch 下载elasticsearch 查看elasticsearch对应的Kibana版本 下载elasticsearch解压后文件目录如下 可执行脚本文件,包括启动elasticsearch服务、插件管理、函数命令等 bin配置文件目录,如elasticsearch配置、角色配置、jvm配置等 conf 默认…

云技术基础学习

声明 学习内容来自 B站 up 主《泷羽 sec》,如涉及侵权等问题,请及时联系,本人将马上删除文章。在此郑重声明,文章仅限于交流学习,任何其他违法行为与本人及泷羽 sec 无关。请务必遵守法律法规,切莫越过法律…

初识Linux · 匿名管道

目录 前言: 匿名管道 理解为什么? 理解是什么? 理解怎么做? 前言: 引入管道之前,我们引入几个问题,进程通信的相关问题。 第一个是进程之间为什么要通信,对于进程间通信来说&…

MySQL数据库:本地部署数据库以及安装彩虹猫【Navicat】

文章目录 一.安装前准备工作1.下载并解压文件2.修复电脑缺失的文件 二.本地部署MySQL1.先解压mysql-8.0.25-winx64.zip,并把文件放到安装需要的位置,再把my.ini文件放到mysql-8.0.25-winx64的根目录2.修改注册表的根目录信息为自己的安装装路径3.进命令符…

计算机网络作业一

一共8次作业,都挺难的,只能在老师的要求下尽力尝试。 任务:探测Internet (IPv4和IPv6) 1. 探测并估计有多少地址是活动的,解释你的方法并估计误差范围 2. 找到尽可能多的关键地址,然后解释它们是什么,为…

联通10010 阿里滑块 231 分析

声明: 本文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 有相关问题请第一时间头像私信联系我删…

DAY58||110.字符串接龙 |105.有向图的完全可达性 |106.岛屿的周长

110.字符串接龙 110. 字符串接龙 题目描述 字典 strList 中从字符串 beginStr 和 endStr 的转换序列是一个按下述规格形成的序列: 1. 序列中第一个字符串是 beginStr。 2. 序列中最后一个字符串是 endStr。 3. 每次转换只能改变一个字符。 4. 转换过程中的中间字符串…

爬虫补环境案例---问财网(rpc,jsdom,代理,selenium)

目录 一.环境检测 1. 什么是环境检测 2.案例讲解 二 .吐环境脚本 1. 简介 2. 基础使用方法 3.数据返回 4. 完整代理使用 5. 代理封装 6. 封装所有使用方法 jsdom补环境 1. 环境安装 2. 基本使用 3. 添加参数形式 Selenium补环境 1. 简介 2.实战案例 1. 逆向目…

《TCP/IP网络编程》学习笔记 | Chapter 8:域名及网络地址

《TCP/IP网络编程》学习笔记 | Chapter 8:域名及网络地址 《TCP/IP网络编程》学习笔记 | Chapter 8:域名及网络地址域名系统什么是域名?DNS 服务器IP 地址和域名之间的转换使用域名的必要性利用域名获取 IP 地址利用 IP 地址获取域名 基于 Wi…

Liunx:简易版进程池

进程向系统申请资源存在一定的效率问题。系统调用在底层是有成本的。频繁的向操作系统申请资源会造成一定的开销。解决办法是一次性向系统申请你需要的资源,将这些资源在用户层管理维护起来,减少程序频繁的陷入内核。这就是池化的意思,可以简…

百亿AI数字人社会初现:Project Sid展示智能代理文明进化路径

项目背景 Project Sid 是一项开创性的AI代理人文明实验,旨在通过新开发的认知架构 PIANO 探讨AI代理人是否能够在大规模数字社会中实现文明的演进。这项实验不仅展示了社会进步、角色分化、治理体系及文化传播等特征,还揭示了一个包含百亿“数字人类”的社会可能性。 PIANO…

CoCa: Contrastive Captioners are Image-Text Foundation Models

Jiahui Yu† Zirui Wang†{jiahuiyu, ziruiw}google.comVijay Vasudevan Legg Yeung Mojtaba Seyedhosseini Yonghui WuGoogle Research 参考代码链接:https://github.com/lucidrains/CoCa-pytorch 模型效果对比网址:CoCa: Contrastive Captioners are …

HarmonyOS一次开发多端部署三巨头之功能级一多开发和工程级一多开发

功能级一多开发与工程级一多开发 引言功能级一多开发SysCaps机制介绍能力集canlUse接口 工程级一多开发三层架构规范 引言 一次开发多端部署 定义:一套代码工程,一次开发上架,多端按需部署 目标:支撑开发者快速高效的开发多终端设…