JMM简单理解

2023-11-15

JMM(java内存模型)

在这里插入图片描述
代码理解

public class test {
    private static boolean f= false;
    public static void main(String[] args) throws InterruptedException {

        new Thread(()->
        {
            while (!f)
            {
                //死循环
            }
        }).start();

        Thread.sleep(1000);
        new Thread(()->
        {
            System.out.println("修改状态");
            f = true;
        }).start();
    }
}

发现第二个线程就算修改了f的值,线程1依然会无限循环。根据模型图可以分析运行过程

  • 线程A先从主存中读取 f 的值,复制保存在自己的本地内存中
  • 线程A根据本地内存中的 f 的值进行判断是否退出循环
  • 线程B修改了主存中的值
  • 但是线程A依然用的是本地内存中 f 的值进行判断,没有同步主存中的 f 值

八大原子操作

根据上面的案例,每一个读取,赋值给本地都是一个原子操作。分为

  • read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的 load 动作使用。
  • load(载入):把 read 操作从主内存中得到的变量值放入工作内存的变量的副本中。
  • use(使用):把工作内存中的一个变量的值传给执行引擎,每当虚拟机遇到一个使用到变量的指令时都会使用该指令。
  • assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的 write 操作使用。
  • write(写入):作用于主内存的变量,它把 store 操作从工作内存中得到的变量的值放入主内存的变量中。
  • 锁定(lock): 作用于主内存中的变量,将他标记为一个线程独享变量。
  • 解锁(unlock): 作用于主内存中的变量,解除变量的锁定状态,被解除锁定状态的变量才能被其他线程锁定。

针对代码例子可以画出如下的图
在这里插入图片描述

  1. 线程A read 主内存的值到一个临时区域(并不是工作内存)
  2. 线程A load 临时区域的值到工作内存并存储
  3. 线程A use 这个值进行操作
  4. 线程B read 主内存的值到一个临时区域(并不是工作内存)
  5. 线程B load 临时区域的值到工作内存并存储
  6. 线程B use 工作内存中的值
  7. 线程B assign 操作将工作变量的值修改
  8. 线程B store 一个临时区域
  9. 线程B write 最新的值到主内存

如何让boolean标记在线程B修改后让线程A感知到呢?可以使用volatile关键字

private static volatile boolean f= false;

可以这么理解
在这里插入图片描述
在主内存和线程中间有一个缓冲区域,每次数据在读取和写入的时候都会通过缓冲区域,线程A有一个类似在缓冲区域注册了一个监听事件。每当其他线程修改了值,write时,总要经过缓冲区域,这个时候会被监听事件捕获,这样线程A就知道了值被修改过,重新刷新工作内存中的值。

缓存一致性

如果开启了volatile在其他线程修改了值后,会马上进行store,write操作同步回主存。

new Thread(()->
        {
            System.out.println("修改状态");
            f = true;
            //todo 其他的耗时操作
        }).start();

就算在修改值后有进行其他耗时的计算,f的值也会马上写入,让其他线程可见,可以更快的保证数据的一致性

volatile原理

在java翻译成汇编语句的时候会用一个指令lock标记
在这里插入图片描述
被标记的变量会

  • 会将当前处理器缓存的数据立即写回主存
  • 会开启类似总线缓存一致性的功能让其他cpu里缓存了变量的值失效。失效后其他cpu在使用时就会重新读取
  • 提供内存屏障,禁止指令重排

并发编程三大特性

  • 可见性
  • 有序性
  • 原子性

(volatile)就是保证了可见性和有序性(禁止指令重排),但是不保证原子性。

有序性和指令重排

指令重排是指在单线程的情况下,在不影响程序结果的前提下可以改变代码执行的顺序进行修改

x=1
y=1
可以优化为
y=1
x=1

但是在多线程环境中如果改变了运行顺序,很有可能结果就会有错误。

禁止指令重排的两个规则

  • as-if-serial :不管怎么重排序(编译器和处理器为了提高并行度会进行重排序优化),单线程程序的执行结果不能被改变。
  • happens-before (某些代码必须发生在某些代码之前)分为八个规则

dcl 单例锁问题
多线程下有问题的单例,多线程中可能得到一个不完整的对象

public class test {
        // 单例
    private static  test t = null;
        // 私有构造方法
    private test() {
        }
        public static test getInstance1() {
            if (null == t) {
                synchronized (test.class) {
                    if (null == t) {
                        t= new  test();
                    }
                }
            }
            return t;
     }

}

因为 new test()不是一个原子操作
步骤是

  1. 获取内容空间
  2. 初始化值(调用构造方法)
  3. 将值赋值给t

但是可能发生指令重排。顺序变为 1 3 2 ,如果是这样,第二个线程进来获取对象==null条件不成立,直接retrun对象,但是这个时候2的初始化并没有完成。所以可能会获得一个只有空间,但是所有元素都为空的对象。

解决办法就是加上volatile关键字,提供内存屏障,禁止指令重排

private static volatile test t = null;

JMM内存屏障
在这里插入图片描述
类似在读取或者写入直接加上一道墙,墙前墙后的指令不能进行指令重排序。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JMM简单理解 的相关文章

  • java.lang.NoClassDefFoundError:org.apache.batik.dom.svg.SVGDOMImplementation

    我在链接到我的 Android LibGDX 项目的 Apache Batik 库时遇到了奇怪的问题 但让我们从头开始 在 IntelliJ Idea 中我有一个项目 其中包含三个模块 Main Android 和 Desktop 我强调的
  • Java new Date() 打印

    刚刚学习 Java 我知道这可能听起来很愚蠢 但我不得不问 System out print new Date 我知道参数中的任何内容都会转换为字符串 最终值是 new Date 返回对 Date 对象的引用 那么它是如何打印这个的呢 Mo
  • Java中反射是如何实现的?

    Java 7 语言规范很早就指出 本规范没有详细描述反射 我只是想知道 反射在Java中是如何实现的 我不是问它是如何使用的 我知道可能没有我正在寻找的具体答案 但任何信息将不胜感激 我在 Stackoverflow 上发现了这个 关于 C
  • Java - 将节点添加到列表的末尾?

    这是我所拥有的 public class Node Object data Node next Node Object data Node next this data data this next next public Object g
  • 在 java 类和 android 活动之间传输时音频不清晰

    我有一个android活动 它连接到一个java类并以套接字的形式向它发送数据包 该类接收声音数据包并将它们扔到 PC 扬声器 该代码运行良好 但在 PC 扬声器中播放声音时会出现持续的抖动 中断 安卓活动 public class Sen
  • 给定两个 SSH2 密钥,我如何检查它们是否属于 Java 中的同一密钥对?

    我正在尝试找到一种方法来验证两个 SSH2 密钥 一个私有密钥和一个公共密钥 是否属于同一密钥对 我用过JSch http www jcraft com jsch 用于加载和解析私钥 更新 可以显示如何从私钥 SSH2 RSA 重新生成公钥
  • Android MediaExtractor seek() 对 MP3 音频文件的准确性

    我在使用 Android 时无法在eek 上获得合理的准确度MediaExtractor 对于某些文件 例如this one http www archive org download emma solo librivox emma 01
  • 多个 Maven 配置文件激活多个 Spring 配置文件

    我想在 Maven 中构建一个环境 在其中我想根据哪些 Maven 配置文件处于活动状态来累积激活多个 spring 配置文件 目前我的 pom xml 的相关部分如下所示
  • 控制Android的前置LED灯

    我试图在用户按下某个按钮时在前面的 LED 上实现 1 秒红色闪烁 但我很难找到有关如何访问和使用前置 LED 的文档 教程甚至代码示例 我的意思是位于 自拍 相机和触摸屏附近的 LED 我已经看到了使用手电筒和相机类 已弃用 的示例 但我
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • Liferay ClassNotFoundException:DLFileEntryImpl

    在我的 6 1 0 Portal 实例上 带有使用 ServiceBuilder 和 DL Api 的 6 1 0 SDK Portlet 这一行 DynamicQuery query DynamicQueryFactoryUtil for
  • Mockito when().thenReturn 不必要地调用该方法

    我正在研究继承的代码 我编写了一个应该捕获 NullPointerException 的测试 因为它试图从 null 对象调用方法 Test expected NullPointerException class public void c
  • 无法解析插件 Java Spring

    我正在使用 IntelliJ IDEA 并且我尝试通过 maven 安装依赖项 但它给了我这些错误 Cannot resolve plugin org apache maven plugins maven clean plugin 3 0
  • 斯坦福 NLP - 处理文件列表时 OpenIE 内存不足

    我正在尝试使用斯坦福 CoreNLP 中的 OpenIE 工具从多个文件中提取信息 当多个文件 而不是一个 传递到输入时 它会给出内存不足错误 All files have been queued awaiting termination
  • Java列表的线程安全

    我有一个列表 它将在线程安全上下文或非线程安全上下文中使用 究竟会是哪一个 无法提前确定 在这种特殊情况下 每当列表进入非线程安全上下文时 我都会使用它来包装它 Collections synchronizedList 但如果不进入非线程安
  • 如何从泛型类调用静态方法?

    我有一个包含静态创建方法的类 public class TestClass public static
  • 玩!框架:运行“h2-browser”可以运行,但网页不可用

    当我运行命令时activator h2 browser它会使用以下 url 打开浏览器 192 168 1 17 8082 但我得到 使用 Chrome 此网页无法使用 奇怪的是它以前确实有效 从那时起我唯一改变的是JAVA OPTS以启用
  • 声明的包“”与预期的包不匹配

    我可以编译并运行我的代码 但 VSCode 中始终显示错误 早些时候有一个弹出窗口 我不记得是什么了 我点击了 全局应用 从那以后一直是这样 Output is there but so is the error The declared
  • 如何实现仅当可用内存较低时才将数据交换到磁盘的写缓存

    我想将应用程序生成的数据缓存在内存中 但如果内存变得稀缺 我想将数据交换到磁盘 理想情况下 我希望虚拟机通知它需要内存并将我的数据写入磁盘并以这种方式释放一些内存 但我没有看到任何方法以通知我的方式将自己挂接到虚拟机中before an O
  • 节拍匹配算法

    我最近开始尝试创建一个移动应用程序 iOS Android 它将自动击败比赛 http en wikipedia org wiki Beatmatching http en wikipedia org wiki Beatmatching 两

随机推荐

  • 来了,MyBatisPlus的join联表查询!

    来源 juejin cn post 7110405284811522085 使用方法 安装 使用 核心类 MPJLambdaWrapper和MPJQueryWrapper MPJLambdaWrapper用法 MPJQueryWrapper
  • At32f421/gd32f103c6/stmf030

    ADF16ST V5 0遥控器 nf2401 bk2425 2 4g 连接 单片机 spi2 pb port bk3431 蓝牙 连接 单片机 uart3 bk2451 连接单片机 uart1 Q20 A77E BK343 供电控制 R61
  • 分布式系统理论

    说到分布式系统 不得不说集中式系统 传统集中式系统中整个项目所有的东西都在一个应用里面 一个网站就是一个应用 当系统压力较大时 只能横向扩展 增加多个服务器或者多个容器去做负载均衡 避免单点故障而影响到整个系统 集中式最明显的优点就是开发
  • 未授权访问的MongoDB修复方案

    修复方法 1 不要把MongoDB服务器部署在互联网上或者DMZ 开启MongoDB的授权访问 编辑 etc mongo conf 文件 找到 auth true 去掉注释 创建用户管理员 另外再连接MongoDB的时候 public cl
  • Mysql(9)_视图

    1 视图是什么 首先 视图是虚拟的表 是不存在的 若使用jdbc连接它 是会报错的 它本质上是sql语句 其次 物理表是真实存在的表 占用内存空间 视图没有实际的物理记录 而表有 视图 view 是在基本表之上建立的表 它的结构 即所定义的
  • 2D/3D人体姿态估计 (2D/3D Human Pose Estimation)

    1 基本概念 算法改进入口 网络设计 特征流 损失函数 数据集的重要性 只要有一个好的 针对性的数据集 问题都可以解决 过集成新一代AutoML技术 可降低算法试错成本 人体姿态估计 Human Pose Estimation 是指图像或视
  • 字典序及1-n之间的数按字典序排列

    今天在刷LeetCode的时候遇见了一道题 题的要求是 给你一个整数 n 按字典序返回范围 1 n 内所有整数 你必须设计一个时间复杂度为 O n 且使用 O 1 额外空间的算法 开始以为是简单的输出 提交后发现与答案相差甚多 看评论后方了
  • xmind右键无法创建

    xmind右键菜单无法创建 前言 安装xmind 设置鼠标右键菜单 验证是否修复成功 后语 前言 前言 hello 不知大家在安装完xmind后 回到桌面后是否可以右键新建xmind文件 如果不行的话 那我就希望这篇文章能帮到你啦 安装xm
  • for in 循环详解

    for i 循环的作用 for in 语句以任意顺序迭代一个对象的除 Symbol 以外的可枚举属性 包括继承的可枚举属性 for in 是为遍历对象属性而构建的 不建议与数组一起使用 在处理有 key value 数据 用于获取对接的 k
  • 人脸识别demo分析(opencv版本)

    一 faceRecognition接口说明 函数名 faceRecognition 函数描述 人脸识别 参数 int recognitionPic 识别的照片 int targetFaceIndex 目标匹配照片索引值 返回 失败返回 1
  • HTML

    如果想将超出div高度和宽度的内容隐藏就用overflow hidden 如果想让超出的内容显示而div宽高不变 用overflow auto 在原来的div宽高基础上出现滚动条 overflow x hidden 设置宽度超出div宽度后
  • Linux 2.6.19.x 内核编译配置选项简介(转)

    Linux 2 6 19 x 内核编译配置选项简介作者 金步国版权声明本文作者是一位自由软件爱好者 所以本文虽然不是软件 但是本着 GPL 的精神发布 任何人都可以自由使用 转载 复制和再分发 但必须保留作者署名 亦不得对声明中的任何条款作
  • 解决Windows下_findnext()异常

    在windows中 使用文件遍历函数 findnext会报0xC0000005错误 原因 findnext 第一个参数 路径句柄 返回的类型为intptr t long long 要改为long long或者intptr t 获取特定格式的
  • 修改MySQL 数据库名称

    MySQL不支持直接修改数据库名称语法 那么要修改数据库名称该如何操作呢 例如 我们将数据库test 修改为test2 第一步 创建新名称对应的数据库 create database if not exists test2 第二步 获取所有
  • 【JDK】Java环境搭建,配置环境变量

    文章目录 1 JDK的下载与安装 1 1 下载JDK 1 2 安装JDK 1 2 1 压缩版JDK 1 2 2 安装版JDK 2 配置环境变量 2 1 打开环境变量 2 2 修改环境变量 2 2 1 新建 JAVA HOME 变量 2 2
  • 非常好用的 文件上传控件

    http fex baidu com webuploader document html
  • Java 重写 equals和hashcode

    重写equals方法的时候为什么需要重写hashcode
  • SqlServe--从字符串中提取数字

    1 基础使用 声明一个nvarchar类型的变量并赋值 declare Name nvarchar 50 set Name 我正在123学 习22 SQL中11 的一些函数 patindex函数返回所查内容在字符串中第一次出现的内容 pri
  • STM32 (十五)ESP8266WIFI

    简介 1 ESP8266wifi 模块 低功耗串口WiFi模块ESP8266内置一个Tensilica 泰思立达 Xtensa架构的32位处理器L106 具有5级流水线 ARM CortexM3是3级流水线 最大时钟速度为160MHz 可以
  • JMM简单理解

    JMM java内存模型 代码理解 public class test private static boolean f false public static void main String args throws Interrupte