5. Java内存模型JMM

news2024/11/18 20:30:00

文章目录

  • 计算机硬件存储体系
    • 基于计算机存储结构的 JMM
  • Java 内存模型 JavaMemoryModel
  • JMM规范下的三大特性
    • 原子性
    • 可见性
    • 有序性
  • 多线程对变量的读写过程
    • 读取过程
  • 多线程先行发生原则 happens-before
    • x,y 的 case 说明
    • happens-before 原则说明
    • happens-before 大原则
    • happens-before 的 8 条原则
      • 次序规则
      • 锁定规则
      • volatile 变量规则
      • 传递规则
      • 线程启动规则(Thread Start Rule)
      • 线程中断规则(Thread Interuption Rule)
      • 线程终止规则(Thread Termination Rule)
      • 对象终结规则(Finalizer)
    • Summary
    • case

大厂面试题

  • 什么是 Java 内存模型?
  • JMM 与 volatile 之间的关系?
  • JMM 有些特性(JMM 的三大特性是什么)
  • 为什么要有 JMM?(JMM 的能干什么?)(JMM 为什么出现?)
  • happens-before 先行发生原则是什么?

计算机硬件存储体系

  • 计算机存储结构,从本地磁盘到主存,再到 CPU 缓存,也就是硬盘到内存,到 CPU
  • 一般对应的程序的操作就是从数据库查数据到内存,然后 CPU 进行计算
  • CPU 中集成众多寄存器


基于计算机存储结构的 JMM

  • CPU 对内存的读写操作的数据不一致性问题
    • CPU 对数据的操作由外存->内存->三级缓存 3 个阶段,对内存读写操作存在数据不一致问题

  • question :
    • 如何解决因为硬件与操作系统数据读写速度不同导致的数据不一致问题?
  • answer :
    • JVM 定义了 JMM 用于屏蔽各种硬件与操作系统的内存访问差异,实现 JVM 跨平台达到一致的内存访问效果

Java 内存模型 JavaMemoryModel

JMM (Java 内存模型 JavaMomary Model,简称 JMM),

  • 本身是一种抽象的概念并不真实存在,仅仅描述了一组约定或规范,(本质)
  • 通过这组规范定义了程序中(特别是多线程)各个变量的读写访问方式,并决定了一个线程对共享变量的何时写入,以及如何变成对另一个线程可见(作用方式)
  • 关键技术点都是围绕多线程的原子性,可见性和有序性展开的(核心,原则)
  • 作用
    • 实现线程和主内存之间的抽象关系
    • 屏蔽底层硬件和操作系统的内存访问差异,实现 JVM 跨平台内存访问的一致性

JMM规范下的三大特性

原子性

  • 指的是一个操作不可被打断,多线程环境下,操作不能被其它线程所干扰

可见性

  • 当一个线程修改了某个共享变量的值,其他线程是否能够立即知道该变更
    • JMM 规范规定所有变量都存储在主内存中

  • 读写过程
    • 线程 A 先从主内存中读取到一个共享变量到自己的线程域内存中,作为本地共享变量的副本
    • 在本地线程域内存对本地共享变量进行修改
    • 将修改后的数据写回到主内存中
    • 线程 B 对线程 A 的更新进行读取
  • 设计思想
    • 系统主内存共享变量数据的修改被写入的时机是不确定的,多线程并发下可能存在"脏读"
    • 因此,每个线程都有自己的线程域,在线程域中保存共享变量副本
    • 线程对变量的所有操作都必须在本地线程域中进行
    • 不能直接读写主内存中的变量,不同线程之间也无法直接访问对方线程域中的变量
    • 线程间变量值的传递依赖主内存进行实现

  • 线程脏读现象

  • 可见性基于该现象,实现线程通知机制,当一个内存共享变量被修改时,其它线程获取修改通知
  • 其它方法
    • 使用基于悲观锁的加锁操作
    • 使用原子类变量

有序性

  • 对于一个线程执行的代码一般有悖于普通顺序从上到下执行,为了提升性能,编译器和处理器通常会对指令序列进行重排序
  • Java 规范规定 JVM 线程内部维持顺序化语义
    • 允许指令执行顺序与代码顺序的不一致性
    • 只需保证指令执行结果与代码顺序化执行结果一致即可
    • 该过程称为指令重排序
  • 优缺点
    • JVM 能根据处理器特征(CPU 多级缓存 Cache,多核处理器),适当对机器指令进行重排序,使机器指令能更符合 CPU 的执行特征,最大限度发挥机器性能
    • 指令重排可以保证串行语义一致,但不保证多线程间语义一致(可能产生脏读)
  • 重排序在代码执行流程中的位置

  • 串行单线程中代码顺序与重排序后指令执行结果一致
  • 处理器在进行重排序时必须要考虑指令之间的数据依赖
  • 多线程环境中线程交替执行,由于编译优化重排的存在,两个线程间使用的变量能发保持一致无法确定,结果存在随机性,某些情况下需要禁止指令重排序

多线程对变量的读写过程

读取过程

  • JVM 运行程序的实体是线程
    • 每个线程创建时 JVM 都会为其创建工作内存(又称为栈空间)
    • 工作内存是每个线程的私有数据区
  • Java 内存模型规定所有的变量均存储在主内存中
    • 主内存是共享内存区域,所有线程均可访问
  • 线程对于变量的操作须在工作内存中进行
    • 首先将主内存的变量复制到工作内存中,对变量完成操作后,将变量写回主内存
    • 不能直接操作主内存中的变量
    • 各线程的工作内存均保存主内存的变量的副本
    • 各线程间无法访问各种线程的工作内存,通过主线程传递变量

多线程先行发生原则 happens-before

在 JMM 中,如果一个操作执行的结果需要对另一个操作可见,或是代码重排序
则两个操作之间必须存在 happens-before(先行发生原则),逻辑上的先后关系

x,y 的 case 说明

x=5线程 A 执行
y=x线程 B 执行
上述称之为:写后续y 是否等于 5 呢?
  • 如果线程 A 的操作(x=5)happens-before 线程 B 的操作(y=x),那么可以确定线程 B 执行后 y=5 必定成立
  • 若不存在 heppens-before 原则,则 y=5 不一定成立
  • happens-before 对程序而言,包含了可见性和有序性的约束

happens-before 原则说明

Java 规范下 JMM 的 happens-before 原则,是判断数据是否存在竞争,线程是否安全的非常重要的手段
依赖此原则,代码编写过程就无需时时处处添加 volatile 和 synchronized 保证程序的有序性;仅通过 happens-before 原则下的规则就可以解决并发环境下两个操作之间是否可能存在冲突的所有问题,无需陷入晦涩难懂的底层编译原理中

happens-before 大原则

  • 一个操作 happens-before 另一个操作,第一个操作的执行结果对第二个操作可见
    • 第一个操作的执行顺序在第二个操作之前
  • 两个操作之间存在 happens-before 关系,并不意味着一定要按照 happens-before 原则制定的顺序来执行
    • 若重排序后的执行结果与 happens-before 一致,则重排序合法

happens-before 的 8 条原则

次序规则

  • 一个线程内,按照代码顺序,写在前面的操作,先行发生于写在后面的操作
  • 线程内部,串行,顺序结构

锁定规则

  • 一个 unLock 操作 happens-before 后面(时间上的先后)对同一个锁的 lock 操作
  • 同一把锁,若存在一次 lock 则需要先释放,才能再加锁(除非可重入锁)

volatile 变量规则

  • 对于一个 volatil 变量的写操作先行发生于后面对该变量的读操作,则前面的写对后面的读是可见的
  • 先写后读,写的操作,对读操作可见

传递规则

  • 若 A 先行发生于 B,B 先行 发生于 C,则 A 先行发生于 C

线程启动规则(Thread Start Rule)

  • Thread 对象的 start()方法优先发生于线程的每一个动作
  • Thread 线程的所有操作都基于是否成功执行了 start()

线程中断规则(Thread Interuption Rule)

  • 对线程 interupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
    • 先调用 interupt()(仅将标志位设置为 true),后面 cpu 看心情中断线程
  • 通过 Thread.interupted()检测到是否发生中断

线程终止规则(Thread Termination Rule)

  • 线程中的所有操作先行发生于对此线程的终止检测
  • 通过 isAlive 等方式检测线程是否已经终止执行

对象终结规则(Finalizer)

  • 一个对象的初始化完成(构造方法执行结束),先行发生于它的 finalize()方法的开始
    • finalize()方法,通常目的是对象不可撤回的丢弃之前执行的清理操作
  • 对象没有完成初始化之前,不能调用 finalize 方法

Summary

  • 在 Java 中 Happens-Before 的语义本质上就是一种可见性
  • A Happens-Before B 意味着 A 的方式过的事情对 B 而言是可见的,无论 A 与 B 两个事件是否在同一个线程中
  • JMM 对 Happens-Before 的设计
    • 针对程序员,设计了 happens-before8 条规则
    • 针对 JVM,为了尽可能少的对编译器和处理器约束而提高性能,JMM
      • 在不影响程序执行结果的前提下对其不做要求(允许指令重排序)

case

image.png

假设存在线程 A 和 B
线程 A 调用 setValue()
线程 B 调用同一个对象 getValue()
则线程 B 的到的返回值是什么?

  • analysis
  • 两个方法不在同一个线程中,不满足次序规则(单线程)
  • 两个方法没有加锁,不满足锁定规则
  • 变量没通过 volatile 修饰,不满足 volatile 变量规则
  • 不满足传递规则
  • 不涉及线程生命周期和对象终结规则
  • anwser

由于不满足 happens-before 原则,
虽然可以确定 A 线程优先于 B 线程执行,但是无法确认 B 线程最终得到的结果
该代码是线程不安全的

  • 修复方案 1
    • 加锁,将 get 和 set 方法加上悲观锁
    • 弊端,写读均为独占锁,读操作使得代码效率下降,适合并发量小的场景
  • 修复方案 2
    • 将 value 定义为 volatile 变量
    • 由于 setter 方法对 value 的修改不依赖于 value 原值,满足 volatile 关键字使用场景

image.png


在这里插入图片描述

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

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

相关文章

TCP重传机制、滑动窗口、拥塞控制

一、总述 TCP,Transmission Control Protocol,是一个面向连接、基于流式传输的可靠传输协议,考虑到的内容很多,比如数据包的丢失、损坏、分片和乱序等,TCP协议通过多种不同的机制来实现可靠传输。今天,重点…

数据库原理实验课(1)

目录 实验内容 安装头歌中的相关内容 具体过程 完结撒花~ 我也是第一次接触oracle的相关软件和操作,所以是一次傻瓜式教学记录 实验内容 安装头歌中的相关内容 具体过程 这是我在百度网盘中下载解压出来的oracle文件夹内的全部内容(可能有因为安装完…

C++指针(四)万字图文详解!

个人主页:PingdiGuo_guo 收录专栏:C干货专栏 前言 相关文章:C指针(一)、C指针(二)、C指针(三) 本篇博客是介绍函数指针、函数指针数组、回调函数、指针函数的。 点赞破六…

【校园安全】支小蜜防校园霸凌语音识别系统的好处

在校园安全领域,防校园霸凌语音识别系统的出现,为预防和应对校园霸凌行为提供了新的技术手段。本文将探讨防校园霸凌语音识别系统的好处,并分析其在校园安全建设中的重要作用。 通过安装在校园各个角落的语音识别设备,系统能够捕…

LeetCode 刷题 [C++] 第3题.无重复字符的最长子串

题目描述 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 题目分析 可以使用滑动窗口加哈希表来实现: 使用start和end两个变脸来表示滑动窗口的头部位置和尾部位置,两者开始均为0;借助哈希表来记录已经遍…

WinSCP下载安装并结合内网穿透实现固定公网TCP地址访问本地服务器

文章目录 1. 简介2. 软件下载安装:3. SSH链接服务器4. WinSCP使用公网TCP地址链接本地服务器5. WinSCP使用固定公网TCP地址访问服务器 1. 简介 ​ Winscp是一个支持SSH(Secure SHell)的可视化SCP(Secure Copy)文件传输软件,它的主要功能是在本地与远程计…

走向审计4.0:内部审计数字化转型的路径与方法【文末送书-34】

文章目录 走向审计4.0:内部审计数字化转型的路径与方法一、内部审计的发展阶段二、内部审计的逻辑架构三、内部审计数字化转型面临的问题四、内部审计数字化转型的框架方法五、内部审计的数字化转型能力体系六、内部审计的数字化转型路径七、内部审计的数字化系统平…

Research Agent:具有解决基于大型文本语料库的复杂问题,具备深度多跳推理的能力

原文地址:https://towardsdatascience.com/the-research-agent-4ef8e6f1b741 2023 年 8 月 29 日 问题简介 在2021年,开始应对基于大量文本回答问题的挑战。在预训练transformers之前的时代,这个问题很难破解。 人工智能和大型预训练tran…

143.和弦是什么?和声是什么?三和弦

内容参考于: 三分钟音乐社 上一个内容:142.音程的构唱练习 和弦的定义: 一个音可以把它称为单音 两个音可以把它称为音程 更多的音,通俗的定义上,三个音或者三个以上的音构成的集体就可以叫做和弦,这些音…

Redis进阶(三):主从复制

为了解决单点问题,实现多服务器部署redis,有几种解决方案可以实现:主从复制,主从哨兵还有集群。 何为主从复制 简单来说有三个服务器分别部署了redis-server程序,选中一个服务器当作主节点,其他的就是从节…

TypeScript 哲学 - Object Types

readonly 修饰对象和数组的 双向可分配性是不同的 Combind types 交叉类型对 值类型可以识别 never 但是 Object 类型识别不了 Tuple

AI时代PPT如何制作?用这10款pptai生成器一键制作!

ppt如何制作? 这可能是很多职场人或大学生日常头疼的问题,职场上随便一个工作汇报、提案展示、团队会议,学校里的小组作业、论文答辩等场景,都会用到ppt。 都说人是视觉动物,在两份文档内容质量一致的情况下&#xf…

Qt插件之输入法插件的构建和使用(二)

文章目录 主键盘搭建Google开源引擎音节分割工具类参考项目下载搭建好各个基础控件之后,就可以开发输入法的主界面和引擎了,这也是输入法的核心。 主键盘搭建 输入法的主界面本质上是一个QStackedWidget容器,将各个类型的输入键盘插入到容器中,然后根据业务需要切换不同的…

通过对话式人工智能打破语言障碍

「AI突破语言障碍」智能人工智能如何让全球交流无障碍 在当今互联的世界中,跨越语言界限进行交流的能力比以往任何时候都更加重要。 对话式人工智能(包括聊天机器人和语音助手等技术)在打破这些语言障碍方面发挥着关键作用。 在这篇博文中&am…

Java对接海康威视摄像头实现抓图

目录 一、下载SDK 二、拷贝示例代码 三、拷贝库文件 四、运行Demo 五、抓图业务 六、调参 ​七、发布Linux正式环境 一、下载SDK 海康开放平台 二、拷贝示例代码 三、拷贝库文件 这时候直接运行ClientDemo会报错,因为缺失库文件! 四、运行Demo …

13 OpenCv自定义线性滤波

文章目录 卷积算子示例 卷积 卷积是图像处理中一个操作,是kernel在图像的每个像素上的操作。Kernel本质上一个固定大小的矩阵数组,其中心点称为锚点(anchor point) 把kernel放到像素数组之上,求锚点周围覆盖的像素乘积之和(包括锚…

杨辉三角(附html,Python,c++杨辉三角代码)

1、前言 杨辉三角很早学编程就知道,但不知道杨辉是古人! 一次偶然的机会,和杨辉三角对上了眼神——杨辉!究竟你是怎么发现杨辉三角的呢? 于是经过了长达3个月锲而不舍的探究。 终究也没发现自己想要的最终结果。 …

内螺旋面的计算

内螺旋面的计算 内螺旋面的计算是机械工程和数学领域中的一个重要问题。它涉及到三维几何、微积分和数值计算等多个方面的知识。内螺旋面的形状和特性对于许多机械装置和工程应用来说至关重要,因此准确计算内螺旋面的几何参数和性质至关重要。内螺旋面是一种特殊的…

warning: #188-D: enumerated type mixed with another type

警告解释:枚举类型混合了其它的数据类型; 解决方法: 1:检查代码,是不是存在混用;; 2:结构体初始化为 0 报warning,不能将结构体的第一个变量,使用枚举类型&am…

Dgraph 入门教程五(Shema)

要想做好图数据库,必须做图数据库设计,想做好Dgraph,就需要设计好Shema。Shema的语法是必须理解,如果不理解,别人写的Shema,我们也看不懂。我这里也是走马观花式的记录,细节还需要在使用的时候去…