5. Java内存模型JMM

文章目录

  • 计算机硬件存储体系
    • 基于计算机存储结构的 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.hqwc.cn/news/522230.html

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

相关文章

VBA_MF系列技术资料1-395

MF系列VBA技术资料1-395 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧,我参考大量的资料,并结合自己的经验总结了这份MF系列VBA技术综合资料,而且开放源码(MF04除外),其中MF01-0…

Devc++调试窗口,一闪而过,调试闪退解决办法

今天使用Dev C调试,发现直接给我闪退了,恼火 解决办法: 安装图片所示框框,从上到下选择: 退出重启,OK

【粉丝福利】探秘内部审计数字化之道:精准解析转型方法与成功路径

🌼前言 内部审计是一种独立的、客观的确认和咨询活动,包括鉴证、识别和分析问题以及提供管理建议和解决方案。狭义的数字化转型是指将企业经营管理和业务操作的各种行为、状态和结果用数字的形式来记录和存储,据此再对数据进行挖掘、分析和应…

循环链表的约瑟夫问题(牛客)——C语言解决

循环链表的约瑟夫问题描述 编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数,报到 m 的人离开。 下一个人继续从 1 开始报数。 n-1 轮结束以后,只剩下一个人,问最后留下的这个人编号是多少? 数据范围: 1 …

论文解读:Rectifying the Shortcut Learning of Background for Few-Shot Learning

文章汇总 问题&动机&解决方法 图像背景是一种有害知识的来源,这是少数镜头学习模型容易吸收的(问题) 通过在训练和评估中提取图像中的前景目标而不需要任何额外的监督来解决这个问题(动机) 在训练和评估时将模型的注意力吸引到图像前景上(方法) 摘要 …

OpenTenBase 开发环境搭建及Debug设置

最近有个 OpenTenBase开源核心贡献挑战赛 领导建议大家都去试试,我也去凑了下热闹,发现能力有限一时半会是搞不明白了,最多也就是能搞搞文档翻译,或者写点操作手册啥的。 不过不管怎么样,先把开发环境搭上,…

Zabbix(四)

Zabbix Proxy zabbix作为一个分布式监控系统(分布式监控解决方案),支持通过代理(proxy)收集zabbix agent的监控数据,然后由zabbix proxy再把数据发送给zabbix server,也就是zabbix proxy 可以代替zabbix server收集监控数据,然后…

Chrome安装Axure插件

打开原型目录/resources/chrome,重命名axure-chrome-extension.crx,修改后缀为rar,axure-chrome-extension.rar 解压到axure-chrome-extension目录打开Chrome,更多工具->扩展程序,打开开发者模式,选择加…

用BIO实现tomcat

一、前言 本课程的难度较高,需要将Servlet原理和IO课程全部学完。 二、当前项目使用方式 (1).自定义servlet 自定义servlet需要实现WebServlet并且实现name和urlMapping 重启进行访问 http://localhost:8090/myServlet (2).自定义html 重启进行访问 http://loc…

洗地机热门品牌测评:石头/希亦/顺造/云鲸洗地机哪个好?哪个牌子最值得入手

大家好,博主是一个热爱生活和科技的居家测评博主。在过去的三年多时间里,我专注于清洁家电——洗地机。我深入测评了三十多款洗地机,现在我将用简单易懂的语言来分享洗地机的选购技巧和热门洗地机机型推荐。 洗地机选购技巧: 看…

在迁移测试中,源表、中间表、目标表的迁移规则

以银行的核心系统从旧核心系统更换为新核心系统为基础,对导入到迁移环境的生产数据(已脱敏)进行数据的验证。 数据迁移环境 迁移环境需要A、B两套环境。其中,A环境为新系统环境、B环境为老系统环境。 数据迁移小组 迁移小组&a…

图片速览 BitNet: 1-bit LLM

输入数据 模型使用absmax 量化方法进行b比特量化,将输入量化到 [ − Q b , Q b ] ( Q b 2 b − 1 ) \left[-Q_{b},Q_{b}\right](Q_{b}2^{b-1}) [−Qb​,Qb​](Qb​2b−1) x ~ Q u a n t ( x ) C l i p ( x Q b γ , − Q b ϵ , Q b − ϵ ) , Clip ⁡ ( x , a , b ) ma…