Java 内存模型

Java 内存模型

大家好,又见面了,我是全栈君。

在多核时代,如何提高CPU的性能成为了一个永恒的话题,而这个话题的讨论主要就是如何定义一个高性能的内存模型,内存模型用于定义处理器的各层缓存与共享内存的同步机制及线程和内存交互的规则。

Java的世界也有属于它自己的内存模型,Java内存模型,即Java Memory Model,简称JMM。由于Java被定义成一种跨平台的语言,所以在内存的描述上面也要能是跨平台的,Java虚拟机试图定义一种统一的内存模型,能将各种底层硬件及操作系统的内存访问差异进行封装,使Java程序在不同硬件及操作系统上都能达到相同的并发效果。它描述了程序中各个变量之间的关系,包括实例域、静态域、数据元素及在实际计算机系统中将变量存储到内存和从内存中取出变量的底层细节。

为更好理解JMM的工作机制,由图带入,从整体上看有几个比较重要的主体,主存、工作内存、变量、变量副本、线程等。首先看主存与工作内存及他们的关系,主存保存了java程序的所有变量,当然这个变量不包括局部变量和方法参数,而工作内存则包含了这些变量的副本;其次是线程与工作内存的关系,每个线程都有一个属于自己的工作内存,不同线程之间的工作内存是互相不可见的,且线程对变量的操作也只能是针对自己的工作内存;最后是关于线程之间的通信机制,由于线程之间不可直接传递,假如一条线程对一个变量进行重新赋值,那么只能通过如下途径让另外一条线程知道,线程一将变量改变反应到主存中,线程二再从主存中读取,这样就基本完成了线程之间的通信了。

JMM定义了八个操作来完成工作内存与主存的通信。假如一条线程准备对一个变量进行新的赋值操作,它可能会先用lock操作锁住主存中的某个变量(不让其他线程获得此变量的锁,直至使用unlock操作释放该变量的锁),接着使用read操作将变量从主存独到工作内存,紧接着load操作将得到的变量值放到工作内存中的变量副本,use操作则将变量值传给线程执行引擎进行运算操作,assign操作把新的变量值从线程执行引擎中传递到工作内存,继续往下,store操作则把变量值从工作内存传送到主存中,接着write操作将得到的值写入主存相应的变量中,最后使用unlock操作释放变量的锁。

Java内存模型具有三个特性:原子性、可见性和有序性。

  • 原子性,java内存模型保证了read、load、assign、use、store、write六个操作具有原子性,我们可以认为除了long和double类型外,对其他基本数据类型所对应的内存单元的访问读写都是原子的。但由于这个原子性的颗粒度太小,通常情况下我们需要更大颗粒度的原子性,这时就需要用锁来保证了。
  • 可见性,在java内存模型中,简单说如果一条线程更改了共享变量的值,而其他线程能马上知道这个更改,我们则说这个变量具有可见性。一般来说有四种方式能保证变量的可见性,分别为volatile、synchronized、final和锁。首先谈谈volatile,被此关键词声明的变量,每当有任何更改时都将立即同步到主存中,而每个线程要使用这个变量时都要重新从主存刷新到工作内存,这样就确保了变量的可见性(当然,普通变量最终也会同步到主存,再由主存同步到每个线程的工作内存,只是这个最终可能比较“长久”,不能保证可见性);由于synchronized底层也是通过锁进行实现,所以synchronized和锁的本质是一样的,当一个线程释放一个锁时,将会强制刷新工作内存中的变量值到主存中,而当另一个线程获取此锁的时候将会强制重新装载此变量值,当然这两个线程获取的是同一个锁,这样就保证了变量的可见性;被final声明的变量一旦完成初始化,其他线程就能看到这个final变量。其实,可见性其实可以看成是一种机制,线程在进入/退出同步块程序时,它将发送/接收一个变量的更改。

JMM可以说是Java的基础,它的定义将直接影响JVM及java多线程实现的机制,要想深入了解多线程并发中的相关问题现象,对Java内存模型的深入研究是必不可少的。它的定义必须考虑下面几个方面,其一是如何更加有效地提高线程的性能效率;其二是如何将底层物理硬件及操作系统的差异屏蔽掉提供统一的对外概念;最后是如何使它的模型既严谨又宽松,保证语义不会产生歧义和一些优化扩展。

====广告时间,可直接跳过====

鄙人的新书《Tomcat内核设计剖析》已经在京东预售了,有需要的朋友可以到 item.jd.com/12185360.ht… 进行预定。感谢各位朋友。

=========================

欢迎关注:

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

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/108487.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • oracle中is not null,oracle之is null和is not null的优化「建议收藏」

    oracle中is not null,oracle之is null和is not null的优化「建议收藏」oracle之优化isnull语句一:isnull的优化方法:通过nvl(字段,j)=j的方式,将字段中为空的数据转化为j,从而正常使用索引。具体实现条件则是:iisnull<===>j=nvl(i,j);注意:使用时必须要确保字段的数据不包含j,例如:(age,15)=15,此时有可能age内容是15,此时不可以,j的值要变换,保证不再age的范围之内。函数介绍:nv…

    2022年8月30日
    6
  • ubuntu卸载安装的方式

    ubuntu卸载安装的方式1、查找已经安装的插件查看全部:dpkg-l查看相关:dpkg-l|grep<筛选目标>例如:dpkg-l|grepgcc2、卸载apt-getremove<dpkg-l的结果>例如:apt-getremovelibgcc-4.8-dev注意:卸载过程中会有依赖关系而产生报错,需要自己确认相互依赖的关系,遵循…

    2022年5月29日
    35
  • 开始移动说话_按住说话怎么不能用了

    开始移动说话_按住说话怎么不能用了2019独角兽企业重金招聘Python工程师标准>>>…

    2022年4月19日
    45
  • 史上最全 XMind 8 快捷键大全「建议收藏」

    史上最全 XMind 8 快捷键大全「建议收藏」对于那3名小学生在我背后鬼鬼祟祟小声议论的这件事,其实我是知晓的。但我还是将注意力集中在眼前的屏幕上,力求表现得尽可能好一些,毕竟这局的形势尚未明朗,胜负依旧难分。又是一阵剧烈的连续按键,对方英雄终于败在我的剑下。随着身后的小学生团队发出“哇”的一声惊叹,我感受到了他们向我投来近乎崇拜的目光。我早已习惯小学生们的艳羡,以及被他们赋予的“大神”称号,当然还有他们对我惯常的提问,“大哥哥,怎…

    2022年5月22日
    49
  • Python:暴力激活成功教程密码

    Python:暴力激活成功教程密码简介:暴力激活成功教程密码的方式一般是枚举法进行破译尝试,通过一次次不同的输入尝试从而得出最终的密码,所以密码的长度和复杂性就尤为重要。本文仅以范例,警示大家在创建密码的时候,应当尽量选择组合复杂度高的,有一定长度的密码,而不是123456之类的简单密码。激活成功教程的思路:1、获取目标密码长度,一般为4,6,12,15,18位长度。2、获取目标密码组成的范围,一般为数字,小写字母,大写字母,特殊符号3、组装密码,通过itertools模块组装。Python:常见排列组合问题处理4、枚举破译。例如生活中最常见的

    2022年8月22日
    7
  • 二叉树性质的性质及证明整理

    二叉树性质的性质及证明整理——整理于2020.4.29二叉树的性质及证明性质1:在二叉树的第i层上至多有2(i-1)个结点(i>=1)证明:数学归纳法(1) i=1时只有一个根节点。显然2(i-1)=20=1是对的(2) 假设对所有的j,1<=j<i,命题成立,即第j层上至多有2(j-1)个结点(3)由归纳假设可得:第i-1层上至多有2(i-2)个结点。由于二叉树…

    2022年5月6日
    57

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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