Java内存模型(Java Memory Model,JMM)

​        JMM​​ 是Java虚拟机(JVM)规范中定义的一组规则和规范,用于描述多线程环境下,Java程序中变量的访问和修改行为,尤其是在并发编程中如何保证内存可见性原子性有序性。JMM 是 Java 并发编程的基石,它定义了线程之间如何通过内存进行交互,确保程序在不同平台和处理器架构上具有一致的并发行为。(JMM保证了java并发编程的跨平台)


一、JMM 的核心目标

JMM 的主要目标是解决多线程环境下的三个核心问题:

  1. ​原子性(Atomicity)​:​保证某些操作是不可分割的,要么全部执行,要么全部不执行,不会被其他线程干扰。

  2. ​可见性(Visibility)​:​当一个线程修改了共享变量的值后,其他线程能够立即看到这个修改。

  3. ​有序性(Ordering)​:​程序执行的顺序按照代码的先后顺序执行,避免指令重排序导致的逻辑错误。


二、JMM 的基本概念

1. 主内存与工作内存(重点)

JMM 抽象了线程与主内存之间的关系:

  • ​主内存(Main Memory)​​:所有线程共享的内存区域,存储所有的实例变量、静态变量等。可以类比为计算机中的物理内存。

  • ​工作内存(Working Memory)​​:每个线程都有自己的工作内存(类似于CPU的寄存器或缓存),线程从主内存中读取变量到自己的工作内存中进行操作,操作完成后再将结果写回主内存。工作内存是线程私有的,线程之间不能直接访问彼此的工作内存。

2. 内存间的交互操作(不是重点)

JMM 定义了八种原子操作,用于线程与主内存之间的交互:

  1. ​lock(锁定)​​:作用于主内存的变量,将一个变量标识为一条线程独占的状态。
  2. ​unlock(解锁)​​:作用于主内存的变量,释放一个变量的锁定状态。
  3. ​read(读取)​​:作用于主内存的变量,将一个变量的值从主内存传输到线程的工作内存,以便随后的load操作。
  4. ​load(载入)​​:作用于工作内存的变量,将read操作得到的值放入工作内存的变量副本中。
  5. ​use(使用)​​:作用于工作内存的变量,将工作内存中的一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用变量的字节码指令时就会执行这个操作。
  6. ​assign(赋值)​​:作用于工作内存的变量,将执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  7. ​store(存储)​​:作用于工作内存的变量,将工作内存中的一个变量的值传送到主内存中,以便随后的write操作。
  8. ​write(写入)​​:作用于主内存的变量,将store操作得到的值放入主内存的变量中。

这些操作必须按特定的规则组合执行,以确保内存操作的原子性、可见性和有序性。


三、JMM 与 Java 并发编程的关系

        JMM 为 Java 并发编程提供了一套规范,确保在多线程环境下程序的行为是可预测和一致的。它通过定义内存屏障(Memory Barriers)、happens-before 关系等机制,来控制线程间的内存可见性和操作顺序。

1. Happens-Before 原则

​Happens-Before​​ 是 JMM 中的一个核心概念,用于判断两个操作之间的执行顺序。如果操作A happens-before 操作B,那么操作A的结果对操作B可见,并且操作A在操作B之前执行。

JMM 定义了以下几种 Happens-Before 关系:

  1. ​程序次序规则(Program Order Rule)​
    在同一个线程中,按照程序代码的顺序,前面的操作 happens-before 后面的操作。

  2. ​锁定规则(Monitor Lock Rule)​
    一个线程解锁操作 happens-before 另一个线程对同一个锁的加锁操作。

  3. ​volatile 变量规则(Volatile Variable Rule)​
    对一个 volatile 变量的写操作 happens-before 后续对这个变量的读操作。

  4. ​线程启动规则(Thread Start Rule)​
    Thread 对象的 start() 方法调用 happens-before 启动线程中的任何操作。

  5. ​线程终止规则(Thread Termination Rule)​
    线程中的任何操作 happens-before 其他线程检测到该线程已经终止(通过 Thread.join() 方法或 Thread.isAlive() 返回 false)。

  6. ​线程中断规则(Thread Interruption Rule)​
    对线程 interrupt() 方法的调用 happens-before 被中断线程检测到中断事件(通过 Thread.interrupted() 或 Thread.isInterrupted())。

  7. ​对象终结规则(Finalizer Rule)​
    一个对象的初始化完成 happens-before 它的 finalize() 方法的开始。

  8. ​传递性(Transitivity)​
    如果操作A happens-before 操作B,且操作B happens-before 操作C,那么操作A happens-before 操作C。

​        Happens-Before原则帮助开发者理解和推断多线程程序中的内存可见性和操作顺序,从而编写出正确且高效的并发代码。

2. 内存屏障(Memory Barriers)

        虽然 JMM 在规范层面定义了 Happens-Before 关系,但在实际实现中,JVM 会通过插入​​内存屏障​​来保证这些关系。内存屏障是一种硬件或软件机制,用于控制指令的执行顺序和内存访问的顺序,防止指令重排序和保证内存可见性。

常见的 memory barriers 包括:

屏障类型作用说明
​LoadLoad 屏障​确保 Load1 的数据加载先于 Load2 及后续的加载操作防止 Load2 读取到比 Load1 更旧的数据
​StoreStore 屏障​确保 Store1 的数据存储先于 Store2 及后续的存储操作防止 Store2 覆盖 Store1 的数据
​LoadStore 屏障​确保 Load1 的数据加载先于 Store2 及后续的存储操作防止 Store2 存储的数据比 Load1 读取的数据更旧
​StoreLoad 屏障​确保 Store1 的数据存储先于 Load2 及后续的加载操作防止 Load2 读取到比 Store1 更旧的数据(最耗性能)

        这些屏障在不同的处理器架构上有不同的实现方式,JVM 会根据目标平台插入相应的屏障指令,以保证 JMM 的语义。


四、JMM 与 Java 关键字的关系

        Java 提供了一些关键字和工具来帮助开发者控制内存可见性和线程同步,这些机制在底层依赖于 JMM 的规范。

1. volatile关键字

2. synchronized关键字​

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【swoole Windows 开发(swoole-cli 开发 hyperf)】

先前swoole在Windows平台的开发体验极差,如果在Windows开发swoole的东西可以用docker或者虚拟机,远程开发,体验比较好的是直接Mac或者Linux系统开发。但是作为window平台的钉子户表示我穷。swoole之前已经推出了cygwin64编译成winwods版本的方…

兴达餐饮 酒店 进销存管理系统软件

兴达餐饮 酒店 进销存管理系统软件

Seal Report:一款免费开源的报表工具

Seal Report 是一款基于 C# 语言开发的开源报表工具,可以从各种数据库或 NoSQL 数据源中生成日常报告,并且执行复杂的计划任务。 功能特性 免费开源:源代码托管在 GitHub 上,用户可以自由使用、修改、甚至集成到自己的系统中&…

WebRTC 多媒体 SDP 示例与解析

webRTC中的SDP的Bundlle可能包含一个或者多个媒体块(媒体描述, 源码对应类ContentInfo),从 m 开始到下一个 m 行(或 SDP 结束)之间的所有属性(包括 a)都属于同一个媒体块(media sect…

SpringBoot 启动富文本文字更改

正常来说 SpringBoot启动时候,展示的文字是这个 、 主播这边想要换一个样式,换一个自己自定义的文字 这边换成了自己的博客名字 具体实现操作如下 在项目目录 resources下创建一个名字为banner.txt的文本,这是SpringBoot启动的时候寻找的…

基于结构熵权-云模型的铸铁浴缸生产工艺安全评价

一、评价模型核心思想 结构熵权法 解决传统熵权法忽略指标间结构关系的问题,通过指标层次网络计算权重。 步骤: 构建工艺安全评价指标体系(树状/网络结构) 计算同级指标间的影响度矩阵 引入修正熵权:wj=1−Ej∑(1−Ek)结构影响因子w_j = \frac{1 - E_j}{\sum (1 - E_k)} \…

[Linux]从零开始的vs code交叉调试arm Linux程序教程

一、前言 最近的项目中需要集成rknn的视觉识别,在这之前我并且没有将rknn集成到自己项目的经验。这里我需要在rknn原本demo的基础上我还需要集成自己的业务代码。但是又有一个问题,原本rknn我们都是使用交叉编译编译到开发板上的,并且我们还要…

视频号私信自动化回复插件

给自己的浏览器插件又增加了视频号斯信的自动化回复搜索:程序员老狼主体逻辑就是,不停的点击打招呼和斯信那个tab切换查看有无小红点,有小红点的会话,就点击。查看有无打招呼,有打招呼就点击,抓取昵称和内容…

Web前端实现银河粒子流动特效的3种技术方案对比与实践

文章目录 前端实现银河粒子流动特效的技术原理与实践 引言:银河粒子特效的技术背景与现状 技术发展历史 当前技术现状 技术原理与实现方案 思维导图:银河粒子特效技术架构 1. CSS3实现方案 基础实现代码 性能优化技巧 2. Canvas 2D实现方案 基础实现代码 Canvas高级优化技术 …

Linux:告别Jammy,拥抱Noble!WSL Ubuntu 22.04 到 24.04 LTS 终极升级指南

大家好!如果大家和我一样,是Windows Subsystem for Linux (WSL) 的忠实用户,那么大家一定对Ubuntu在其中的表现印象深刻。我们中的许多人可能还在使用稳定可靠的Ubuntu 22.04 LTS (Jammy Jellyfish)。但现在,一个更令人兴奋的时代…

江协科技STM32 11-1 SPI通信协议

本节课我们将继续学习下一个通信协议,SPI。SPI通信和我们刚刚学习过的I2C通信差不多。两个协议的设计目的都一样都是实现主控芯片和各种外挂芯片之间的数据交流,有了数据交流的能力,我们的主控芯片就可以挂载并操纵各式各样的外部芯片&#x…

预过滤环境光贴图制作教程:第一步 - HDR 转立方体贴图

在基于物理的渲染(PBR)中,环境光贴图是实现真实光照效果的核心组件之一。而将 HDR 全景图转换为立方体贴图,是制作预过滤环境光贴图的基础步骤。本教程将详细讲解如何实现这一转换过程。 什么是 HDR 转立方体贴图? HDR(高动态范围)全景图通常以等矩形投影(Equirectan…

02 深度学习介绍【动手学深度学习v2】| 学习笔记

1、intro自然语言处理虽然我们过去取得了很大的进展,但是实际上还是停留在感知层面。计算机视觉领域,因为图片里面都是像素,像素很难用符号学来解释,所以计算机视觉大部分是用概率模型或机器学习来做。深度学习它是机器学习的一种…

智能学号抽取系统V5.6.4重磅发布

告别随机数,拥抱智能点名!—— 全新升级的“智能学号抽取系统V5.6.4”重磅发布! 摘要: 还在为课堂随机提问、活动抽奖而手动翻名单、查表格而烦恼吗?还在忍受传统点名工具的简陋和不智能吗?今天&#xff0…

Leetcode-141.环形链表

dict和set 1. 结构上的区别:类型键(Key)值(Value)示例dict有有{a: 1, b: 2}set有没有{a, b} dict 是**键值对(key-value)**的集合。set 是只有键(key)没有值的一组唯一元…

调节步进电机速度时调PSC和调ARR的区别

在步进电机控制中,调节速度通常是通过改变脉冲频率实现的。代码中选择调节ARR(Auto-Reload Register)而非PSC(Prescaler)的原因如下: 1. ARR 与 PSC 的核心区别 • ARR(自动重载寄存器&#xff…

在 AKS 中运行 Azure DevOps 私有代理-1

简介 配置 Azure DevOps 私有代理的传统方法是将其部署在虚拟机 (VM) 上。然而,一个有趣的替代方案是利用 Azure Kubernetes 服务 (AKS) 来实现此目的。 本文将指导您如何使用 Helm Chart 在 AKS 集群中设置 Azure DevOps 私有代理,并提供该过程的分步说明。 在 AKS 中部署…

C# _Json数据

目录 1、添加Json库 2、数据序列化(对象转 JSON)和反序列化(JSON 转对象)操作 3、序列化 创建和读取Json数据 创建Json数据 定义一个CreateJson方法 读取 解析 Json数据 定义一个ReadJson方法 4、程序运行结果 在 C# 中&…

JavaScript 原始值与引用值

JavaScript 原始值与引用值 ECMAScript变量可以包含两种不同类型的数据:原始值和引用值。 原始值(primitive value)就是最简单的数据,引用值(reference value)则是由多个值构成的对象。 保存原始值的变量是…

linux中挂载磁盘和卸载

查找磁盘 找到你想要挂载的磁盘。可以使用lsblk或fdisk -l命令来查看系统中所有的磁盘和分区信息。 lsblk 对数据盘进行分区 在fdisk交互界面里,按以下步骤操作 fdisk /dev/vdb- 输入n来创建新分区。 - 按照提示设置分区的起始扇区、结束扇区等信息,…