java内存模型介绍[通俗易懂]

java内存模型介绍[通俗易懂]####Java内存模型Java内存模型描述了Java虚拟机和计算机内存之间是如何协同工作的。一个Java虚拟机也是一个完整的计算机的模型,因此,这个模型自然也包含了内存模型。如果你想写出表现良好的并发程序就必须理解Java内存模型。Java内存模型描述了不同线程间如何和何时看到被其他线程修改的共享变量以及在需要时如何同步访问共享变量。原来的Java内存模型存在很多不足,所以在Java5时进行了修改。这个一直使用至今。####Java内存模型每个运行在Java虚拟机中的线程都拥有自己的线程栈。这

大家好,又见面了,我是你们的朋友全栈君。

####Java内存模型
Java内存模型描述了Java虚拟机和计算机内存之间是如何协同工作的。一个Java虚拟机也是一个完整的计算机的模型,因此,这个模型自然也包含了内存模型。

如果你想写出表现良好的并发程序就必须理解Java内存模型。Java内存模型描述了不同线程间如何和何时看到被其他线程修改的共享变量以及在需要时如何同步访问共享变量。

原来的Java内存模型存在很多不足,所以在Java5时进行了修改。这个一直使用至今。

####Java内存模型

Java_Memory_Model

每个运行在Java虚拟机中的线程都拥有自己的线程栈。这个线程栈包含了这个线程所调用方法的当前执行点,所有我们也可以称之为”调用栈“。在线程执行代码的过程总,调用栈随之发生变化。

线程栈也包含每个被执行的方法中的所有局部变量。一个线程只能访问自己的线程栈。一个线程创建的局部变量对其他线程是不可见的。即使两个线程执行同样的代码,这两个线程任然需要在它们各自的线程栈中创建这些变量。

所有的基本类型的局部变量都全部存放在各自的线程栈中,对其他线程不可见。一个线程可能会向另一个线程传递一个基本类型变量的拷贝,但是这并不能共享基本类型变量自身。

在堆中包含所有你在Java程序中创建的对象。这也包含所有基本类型所对应的装箱类型。即便,我们创建了一个对象然后我们把它赋给了一个局部变量,或者作为另一个对象的成员变量,这个对象仍然存放在堆中。

Java_Memory_Model

一个局部变量可能是基本类型,这样它就永远呆在线程栈中。

一个局部变量也可能是引用变量。在这种情况下,引用变量存放在线程栈中,对象本身存放在堆中。

一个对象可能包含方法,这些方法又可能包含局部变量。这些局部变量也被存放在线程栈中,即便这个方法所属的对象存放在堆中。

一个对象的成员变量随着对象自身存放在堆中。不管这个变量是基本类型还是引用类型都是如此。

静态类变量随着类定义也存放在堆中。

存放在堆中的对象可以被所有的线程通过指向对象的引用访问。当一个线程访问一个对象时,它也可以访问这个对象的成员遍历。如果两个线程在同一时刻调用同一个对象上的同一个方法,它们都可以访问对象的成员变量,但是每个线程都会拥有各自的局部变量拷贝。

Java_Memory_Model

####硬件内存架构
现代硬件内存架构和Java内存模型有一些不一样的地方。理解硬件内存架构和Java内存模型如何和它协同工作也非常重要。
通用的硬件内存架构:

hardware_memory_arch

现代计算机通常拥有两个或多个CPU。这些CPU中可能还有是多核的。这一点,使得多个线程同时运行在一台计算机上称为了可能。每个CPU可以在任何时刻运行一个线程。
这就意味着如果你的程序是多线程的,在你的程序内部,一个线程对应一个CPU可能同时运行。

每个CPU包含一些寄存器。CPU可以在这些寄存器上执行操作会比在内存上快很多。这是因为CPU访问寄存器的速度远高于访问内存的速度。

每个CPU还可能拥有一个CPU缓存层。实际上,现代计算机都会有一个一定大小的缓存层。CPU访问缓存的速度远高于主存,但通常又低于访问寄存器的速度。因此,缓存是用来平衡CPU访问寄存器和主存之间的速度差异的。一些CPU可能拥有多级缓存(一级缓存和二级缓存)。

一台计算机还拥有一块主存区域(RAM)。所有的CPU都可以访问主存。主存区域通常要比CPU的缓存大得多。

通常,当一个CPU需要访问主存的时候,它会将数据从主存读到CPU的缓存中,甚至再从CPU的缓存读到它内部的寄存器中,然后执行相关的操作。当CPU需要将结果写回到主存中时,它会先将值刷新到缓存中,然后在某一时刻刷新回主存中。

当CPU需要在缓存中存储一些其他的东西时,会将存储在缓存中的值刷新回主存中。

CPU缓存可以局部刷新。

####Java内存模型和硬件内存架构之间的联接

正如上面所提到的,Java内存模型和硬件内存架构是不同。在硬件内存架构并不区分内存栈和堆。在硬件中,所有的线程栈和堆都位于主存中。线程栈的一部分和堆可能同一位于CPU缓存和寄存器中。

gap

当对象和变量存放在计算机的不同内存区域中时,就会暴露出一些问题。主要包括两个方面:

  • 内存可见性
  • 当读,检查和写共享变量时的竞争条件

#####内存可见性
如果两个或多个线程共享同一个对象时,在不使用vloatile声明或者同步的情况下,一个线程更新了这个共享对象的值可能对其他线程不可见。

想象一下,这个共享对象最初存放在主存中。运行在一个CPU上的一个线程,将这个共享对象读到它的CPU缓存中。并在缓存中修改了这个共享对象。只要这个CPU缓存还没有刷新回主存,这个共享共享对象变化后的版本对其它CPU的线程来说就是不可见的。这种方式可能使每个线程最终拥有这个共享对象的拷贝,每个拷贝都停留在不同的CPU缓存中。

cache

解决这个问题,可以使用Java中的volatile关键字。volatile关键字可以确保你直接从主存中读取一个给定的变量,当变量发生更新总是会被写回到主存中。

#####竞争条件
如果两个或者多个线程共享一个对象,超过一个对象去更新对象上的变量,竞争条件可能就会发生。

race_condition

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/157183.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 给定一个输入文件,包含40亿个非负整数。产生一个不在该文件中的整数。内存限制:1GB…

    给定一个输入文件,包含40亿个非负整数。产生一个不在该文件中的整数。内存限制:1GB…

  • PHP面试-复习知识点整理

    PHP面试-复习知识点整理

  • 临时手机号接收验证码在线短信接收_临时手机号短信验证码平台

    临时手机号接收验证码在线短信接收_临时手机号短信验证码平台  处在这个前所未有的信息化时代,网络带给我们极大便利的同时,也让我们的个人信息安全也遭受了严重的威胁。很多人对个人信息的保护意识淡薄,不知道当今个人信息泄露的广泛性,没有认识到个人信息泄露的途径以及严重危害。比如我们注册任何一个网站的时候,往往需要提供手机号码,输入接收到的短信验证码,或者邮箱地址也一样。一旦这些信息泄漏,就会经常性地收到一些垃圾信息、广告信息。  但是你为了查看或下载这个网站里面的资源,又不得不注册。怎么办呢?如果有一些匿名、临时、一次性的邮箱地址,以及可以免费收发短信验证码的…

    2022年10月13日
  • aspnetmvc和aspnetcoremvc的一些区别「建议收藏」

    aspnetmvc和aspnetcoremvc的一些区别「建议收藏」1.路由控制器添加特性:[RoutePrefix(“api/controller”)]=>[Route(“api/[controller]”)]方法添加特性:[HttpGet][Route(“{id}”)]=>[HttpGet(“{id}”)]2.返回参数webapiIHttpActionR…

  • 解决MyQL数据库中1045错误的方法——Windows系统

    解决MyQL数据库中1045错误的方法——Windows系统在各种各样的适用场所,MySQL会出现各种各样的问题,经过足足半年的长跑,我的数据库终于修复了Bug,可以重新使用了。数据库出问题,那可能是家常便饭了。经过这足足半年的煎熬,我决定在以后的日子里,记录下我在使用数据库时遇到的色彩缤纷的问题,以及这些问题的解决方法。由此,今天写了这篇博客。首先,给大家看看,这个问题是什么样子的。我在这里用到的MySQL可视化工具为Navicat。这个错误…

  • 初识.Net审计

    初识.Net审计前言对.net认知比较少,学习一下.net的一些简单审计。遇到.net源码能简单审审。基础概念文件类型ASPX.cs是页面后的代码,aspx负责显示,服务器端的动作就是在as

    2021年12月13日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号