返回顶部
首页 > 资讯 > 后端开发 > JAVA >Java——内存模型详解!
  • 348
分享到

Java——内存模型详解!

java开发语言 2023-10-20 21:10:09 348人浏览 泡泡鱼
摘要

Java内存模型是一种抽象的规则或规范,定义了程序中存在竞争现象的对象(包括实例字段、静态字段和数组对象,不包括局部变量,形式参数;后者是线程私有,不存在竞争问题)的访问方式。         如果我们要想深入了解Java并发编程,就要先理

Java内存模型是一种抽象的规则或规范,定义了程序中存在竞争现象的对象(包括实例字段、静态字段和数组对象,不包括局部变量,形式参数;后者是线程私有,不存在竞争问题)的访问方式。

        如果我们要想深入了解Java并发编程,就要先理解好Java内存模型。Java内存模型定义了多线程之间共享变量的可见性以及如何在需要的时候对共享变量进行同步。

        JVM通过Java内存模型,屏蔽掉不同硬件和操作系统的内存访问差异,实现各种平台具有一致的并发效果。

1. 内存模型

1.1 硬件的效率与一致性——计算机内存模型

 

        显然,CPU的运行速度远远大于内存交互速度,至少有这几个数量级的差距。因此,现代计算机在CPU与内存之间都有一个高速缓存(Cache)作为数据缓冲:将运算需要的数据从内存中复制到缓存中,当缓存从CPU中拿到运算结果之后再同步到内存中——这样CPU就不用等待缓慢的内存读写了。

        但此时就出现了一个大问题:缓存一致性。  现在基本都是多核处理器,每个处理器都有自己的高速缓存,而他们共享同一个主内存,这样就会导致各自的缓存数据不一致的情况,为了解决这个问题各个处理器访问缓存时都要遵循一些协议,在读写时要根据协议来进行操作,这类协议有MSI、MESI、MOSI、Synapse等等。  除此之外,为了使得处理器内部的运算单元能尽量被充分利用,处理器可能会对输入代码进行乱序执行优化,处理器会在计算之后将乱序执行的结果重组,保证该结果与顺序执行的结果是一致的,但并不保证程序中各个语句计算的先后顺序与输入代码中的顺序一致。类似的,Java虚拟机的即时编译器中也有类似的指令重排序优化。

1.2 Java内存模型

        接下来,我们通过一个Java程序了解缓存一致性。 

        Java的内存模型跟计算机的内存模型极其相似

 

        CPU在内存模型当中充当的就是“算术逻辑单元”,主要负责运算。  Java线程也可以理解为“运算单元”,因此,可以看到,Java内存模型几乎和计算机内存模型保持一致。

内存模型总述 

所有变量都在主内存当中,工作内存中的变量都是从主内存中拷贝的。

线程对变量的所有操作都在工作内存中完成

不同线程无法直接访问其他线程工作内存中的变量。线程之间的变量传递需要通过主内存完成

        Java中的缓存一致性问题:  .  考虑下面这个代码:  i = 10++;  假设现在有两个线程同时执行上述代码,  根据内存模型总述第1条,这两个线程都是从主内存中拷贝i到自己的工作内存当中,然后分别对其执行自加,然后将其放回主内存。  此时,尽管“自加了两次”,但实际上i=11,而非12。  这种被多个线程访问的变量为共享变量。 

1.3 Java内存模型和计算机内存模型的关系

        通常情况下,当一个cpu需要读取主存的时候它会将主存的部分读取到cpu缓存中。它甚至会将缓存的部分内容读到内部寄存器里,然后在寄存器中执行操作,当cpu需要将结果回写到主存的时候,它会将内部寄存器的值刷新到缓存中,然后在某个时间点将值刷新回主存。

 

        Heap就是主内存,Thread Stack就是工作内存 

        通过图可以看出java内存模型与硬件架构之间存在一些差异,硬件内存架构它没有区分线程栈和堆,对于硬件而言所有的线程栈和堆都分布在主内存里,部分cpu栈和堆可能出现cpu缓存中和cpu内部的寄存器里面。

2. 内存特性

2.1 原子性

        一个操作是不可中断的,即便是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。 

        对基本数据类型的变量读取和赋值的操作都是原子操作,即这些操作是不可中断,要么执行,要么不执行。

x = 10;        //语句1

y = x;        //语句2

x++;          //语句3

x = x + 1;    //语句4

        以上语句只有语句1是原子性操作。

 语句1是直接将数值10赋值给x,也就是说线程执行这个语句会直接将数值10写入到工作内存中。 

语句2实际上包含2个操作,先读取x的值,再将x的值写入到工作内存中,虽然读取x的值及将x的值写入工作内存都是原子操作,但结合起来就不是原子性的操作了。 

x++和x=x+1包含3个操作,读取x的值,进行加1操作,写入新的值。 

也就是说,只有简单的读取、赋值(而且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操作)才是原子操作。 

        Java内存模型只保证了基本读取和赋值是原子性操作,如果要实现更大范围操作的原子性,可以通过synchronized和Lock来实现。由于synchronized和Lock能够保证任一时刻只有一个线程执行该代码块,那么自然就不存在原子性问题了,从而保证了原子性。

2.2 有序性

        编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。  也就是说:在并发时,程序的执行可能会出现乱序。

        另外,Java内存模型具备一些先天的“有序性”,即不需要通过任何手段就能够得到保证的有序性,这个通常也称为 happens-before 原则。如果两个操作的执行次序无法从happens-before原则推导出来,那么它们就不能保证它们的有序性,虚拟机可以随意地对它们进行重排序。

2.3 可见性

        一个线程修改了某一个共享变量的值,其他线程是否能够立刻知道这个修改。(可见会牺牲性能)。

        通过volatile关键字保证可见性:

        当一个共享变量被volatile修饰时,它会保证修改的值会立即更新到主存,当有其他线程需要读取时,它会去主存中读取最新的值。

        普通的共享变量不能保证可见性,因为共享变量被修改后,什么时候被写入主存是不确定的,当其他线程去读取的时候,此时内存中可能还是原来的旧值,因此无法保证可见性。 

        通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。 

2.4 Happen-Before 规则

程序顺序原则:一个线程内保证语义的串行性(按书写次序顺序执行)

锁规则:解锁必然发生在随后的加锁前

传递性:A 先于 B ,B 先于 C,那么 A 必然先于 C

volatile 规则:volatile 变量的写,先发生于读,保证了 volatile 变量的可见性

线程启动规则:线程的 start() 方法先于它的每一个动作

线程中断规则:线程的所有操作先于线程的终结(Thread.join())

线程终结规则:线程的中断先于被中断程序的代码

对象终结规则:对象的构造函数执行结束先于 finalize() 方法

        如果不满足以上规则,则虚拟机就会对其进行指令重排序

指令重排序

        在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序,重排序分为以下三种类型

编译器优化的重排序--编译器在不改变单线程程序语义的情况下,可以重新安排语句的执行顺序

指令级并行的重排序--如果不存在数据依赖,处理器可以改变语句对应机器执行的语句顺序

内存系统的重排序--由于处理器使用缓存和读/写缓存,这使得加载和存储操作看上去像是在乱序执行

        在执行程序时,java内存模型确保在不同的编译器和不同的处理器平台上,来插入内存屏障来禁止特定的编译器重排序和处理器重排序,从而为上层提供内存一致性的条件。

3. 内存同步(交互)的八大操作

        关于主内存与工作内存之间具体的交互协议,即一个变量如何从主内存拷贝到工作内存、如何从工作内存同步回主内存之类的实现细节,Java内存模型中定义了以下八种操作来完成:

lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占的状态。

unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。

read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。

load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。

use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎。(每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作)

assign(赋值):作用于工作内存的变量,它把一个执行引擎接收到的值赋值给工作内存的变量。(每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作)

store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用。

write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。

同步规则

如果要把一个变量从主内存中复制到工作内存,就需要按顺序地执行read和load操作,如果把变量从工作内存中同步回主内存中,就要按顺序地执行store和write操作。但java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行。 

不允许read和load、store和write操作之一单独出现 

不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中。 

不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中。 

一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。 

一个变量在同一时刻只允许一条线程对其进行locd操作,但lock操作可以被同一条线程重复执行多次,多次执行load后,只有执行相同次数的unlock操作,变量才会被解锁。lock和unlock必须成对出现。 

如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行Load或assign操作初始化变量的值。 

如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量。 

对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)。

来源地址:https://blog.csdn.net/m0_64363449/article/details/131845539

--结束END--

本文标题: Java——内存模型详解!

本文链接: https://lsjlt.com/news/433301.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Java——内存模型详解!
    Java内存模型是一种抽象的规则或规范,定义了程序中存在竞争现象的对象(包括实例字段、静态字段和数组对象,不包括局部变量,形式参数;后者是线程私有,不存在竞争问题)的访问方式。         如果我们要想深入了解Java并发编程,就要先理...
    99+
    2023-10-20
    java 开发语言
  • Java内存模型详解
    目录什么是JMM主存与工作内存volatile 关键字有什么用一个线程对共享变量做了修改之后,其他的线程能够看到(感知到)该变量的这种修改(变化)什么是JMM JMM全称Java M...
    99+
    2023-05-18
    Java内存模型 Java JMM模型
  • 详解Java的内存模型
    目录JVM的内存模型Java “一次运行,到处编译” 的真面目JVM的本质和位置JVM的内存模型总览线程私有区域线程共享区域直接内存从例子来理解内存模型JVM的内存模型 Java “...
    99+
    2024-04-02
  • Java内存模型JMM详解
    Java Memory Model简称JMM, 是一系列的Java虚拟机平台对开发者提供的多线程环境下的内存可见性、是否可以重排序等问题的无关具体平台的统一的保证。(可能在术语上与Java运行时内存分布有歧义,后者指堆、方法区、线程栈等内存...
    99+
    2023-05-30
    java 内存模型 详解
  • JAVA内存模型(JMM)详解
    目录前言JAVA并发三大特性可见性有序性原子性Java内存模型真面目Happens-Before规则1.程序的顺序性规则2. volatile 变量规则3.传递性锁的规则5.线程 s...
    99+
    2022-12-08
    JAVA 内存模型 java内存模型和jvm内存模型的区别 java jmm模型
  • Java内存区域与内存模型详解
    这篇文章主要介绍“Java内存区域与内存模型详解”,在日常操作中,相信很多人在Java内存区域与内存模型详解问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java内存区域与内存模型详解”的疑惑有所帮助!接下来...
    99+
    2023-06-02
  • Java内存模型图文详解
    1. 概述多任务和高并发是衡量一台计算机处理器的能力重要指标之一。一般衡量一个服务器性能的高低好坏,使用每秒事务处理数(Transactions Per Second,TPS)这个指标比较能说明问题,它代表着一秒内服务器平均能响应的请求数,...
    99+
    2014-08-03
    Java
  • 详细分析Java内存模型
    目录一、为什么要学习并发编程二、为什么需要并发编程三、从物理机中得到启发四、Java 内存模型五、原子性5.1、什么是原子性5.2、如何保证原子性六、可见性6.1、什么是可见性6.2...
    99+
    2024-04-02
  • Java并发内存模型详情
    目录1、Java内存模型2、硬件内存架构3、实际执行3.1 共享对象可见性3.2 竞争条件Java是一门支持多线程执行的语言,要编写正确的并发程序,了解Java内存模型是重要前提。而...
    99+
    2024-04-02
  • 【JVM】JVM内存模型详解
    一、JVM是什么? JVM是Java Virtual Machine(Java虚拟机)的缩写,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。由一套字节码指令集、一组寄存器、一个栈、一个垃圾回收...
    99+
    2023-09-16
    jvm java 面试
  • Java内存模型之happens-before概念详解
    简介 happens-before是JMM的核心概念。理解happens-before是了解JMM的关键。 1、设计意图 JMM的设计需要考虑两个方面,分别是程序员角度和编译器、处理...
    99+
    2024-04-02
  • java高并发的volatile与Java内存模型详解
    public class Demo09 { public static boolean flag = true; public static class T1...
    99+
    2024-04-02
  • Java内存模型中的happen-before关系详解
    目录前言概述为什么需要 JMM,它试图解决什么问题?JMM 是怎么解决可见性等问题的呢?后记前言 Java 语言在设计之初就引入了线程的概念,以充分利用现代处理器的计算能力,这既带来...
    99+
    2022-11-13
    Java happen before Java 内存模型
  • GoLang内存模型详细讲解
    目录栈内存-协程栈-调用栈逃逸分析go 堆内存堆如何进行分配go 语言对象的垃圾回收如何减少GC对性能的分析GC 优化效率栈内存-协程栈-调用栈 为什么go的栈是在堆上? go 协程...
    99+
    2022-12-15
    GoLang内存模型 Go内存模型
  • Java 内存模型(JVM)
    目录前言一、什么是 Java 内存模型二、为什么需要 Java 内存模型三、顺序一致性内存模型四、Happens-Before 规则前言 在并发编程中,当多个线程同时访问同一个共享...
    99+
    2024-04-02
  • 怎么理解Java内存模型
    本篇内容主要讲解“怎么理解Java内存模型”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么理解Java内存模型”吧!内存模型的由来 计算机在执行程序的时候每条指令都是由CPU来执行的。而CPU...
    99+
    2023-06-16
  • 详解JVM系列之内存模型
    目录1. 内存模型和运行时数据区2、思维导图和图例3、对象向JVM申请空间4、为什么需要Survivor区?5、为什么需要两个Survivor区?6、例子进行验证堆内...
    99+
    2024-04-02
  • Java内存模型的深入讲解
    目录内存模型硬件架构Java内存模型与硬件关联对象的可见性竞争条件总结Java内存模型展示了Java虚拟机是如何与计算机内存交互的,解决多线程读写共享内存时资源访问的问题。 内存模型...
    99+
    2024-04-02
  • Java内存模型该怎么理解
    这篇文章主要讲解了“Java内存模型该怎么理解”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java内存模型该怎么理解”吧!1.为什么会误解首先,我们先来分析一下问什么很多人,甚至是大多数人...
    99+
    2023-06-16
  • Java内存模型final的内存语义
    目录1、final域的重排序规则final2、写final域的重排序规则3、读final与的重排序规则4、final域为引用类型5、为什么final引用不能从构造函数内“逸出”6、f...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作