Java并发指南6:Java内存模型JMM总结
本文转载自互联网,侵删
在前面的文章中我们介绍了Java并发基础和线程安全的概念,以及JMM内存模型的介绍,包括其定义的各种规则。同时我们也介绍了volatile在JMM中的实现原理,以及Lock锁和synchronized实现同步方式的区别。最后还讲述了final关键字在JSR-133中的语义加强。
介绍了这么多内容,主要还是围绕着JMM来讲的,所以本文再次对JMM做一个总结。
处理器内存模型
顺序一致性内存模型是一个理论参考模型,JMM和处理器内存模型在设计时通常会把顺序一致性内存模型作为参照。JMM和处理器内存模型在设计时会对顺序一致性模型做一些放松,因为如果完全按照顺序一致性模型来实现处理器和JMM,那么很多的处理器和编译器优化都要被禁止,这对执行性能将会有很大的影响。
根据对不同类型读/写操作组合的执行顺序的放松,可以把常见处理器的内存模型划分为下面几种类型:
- 放松程序中写-读操作的顺序,由此产生了total store ordering内存模型(简称为TSO)。
- 在前面1的基础上,继续放松程序中写-写操作的顺序,由此产生了partial store order 内存模型(简称为PSO)。
- 在前面1和2的基础上,继续放松程序中读-写和读-读操作的顺序,由此产生了relaxed memory order内存模型(简称为RMO)和PowerPC内存模型。
注意,这里处理器对读/写操作的放松,是以两个操作之间不存在数据依赖性为前提的(因为处理器要遵守as-if-serial语义,处理器不会对存在数据依赖性的两个内存操作做重排序)。
内存模型名称 |
对应的处理器 |
Store-Load 重排序 |
Store-Store重排序 |
Load-Load 和Load-Store重排序 |
可以更早读取到其它处理器的写 |
可以更早读取到当前处理器的写 |
TSO |
sparc-TSO X64 |
Y |
Y |
|||
PSO |
sparc-PSO |
Y |
Y |
Y |
||
RMO |
ia64 |
Y |
Y |
Y |
Y |
|
PowerPC |
PowerPC |
Y |
Y |
Y |
Y |
Y |
下面的表格展示了常见处理器内存模型的细节特征:
在这个表格中,我们可以看到所有处理器内存模型都允许写-读重排序,原因在第一章以说明过:它们都使用了写缓存区,写缓存区可能导致写-读操作重排序。同时,我们可以看到这些处理器内存模型都允许更早读到当前处理器的写,原因同样是因为写缓存区:由于写缓存区仅对当前处理器可见,这个特性导致当前处理器可以比其他处理器先看到临时保存在自己的写缓存区中的写。
上面表格中的各种处理器内存模型,从上到下,模型由强变弱。越是追求性能的处理器,内存模型设计的会越弱。因为这些处理器希望内存模型对它们的束缚越少越好,这样它们就可以做尽可能多的优化来提高性能。
由于常见的处理器内存模型比JMM要弱,java编译器在生成字节码时,会在执行指令序列的适当位置插入内存屏障来限制处理器的重排序。同时,由于各种处理器内存模型的强弱并不相同,为了在不同的处理器平台向程序员展示一个一致的内存模型,JMM在不同的处理器中需要插入的内存屏障的数量和种类也不相同。下图展示了JMM在不同处理器内存模型中需要插入的内存屏障的示意图:
如上图所示,JMM屏蔽了不同处理器内存模型的差异,它在不同的处理器平台之上为java程序员呈现了一个一致的内存模型。
JMM,处理器内存模型与顺序一致性内存模型之间的关系
JMM是一个语言级的内存模型,处理器内存模型是硬件级的内存模型,顺序一致性内存模型是一个理论参考模型。下面是语言内存模型,处理器内存模型和顺序一致性内存模型的强弱对比示意图:
从上图我们可以看出:常见的4种处理器内存模型比常用的3中语言内存模型要弱,处理器内存模型和语言内存模型都比顺序一致性内存模型要弱。同处理器内存模型一样,越是追求执行性能的语言,内存模型设计的会越弱。
JMM的设计
从JMM设计者的角度来说,在设计JMM时,需要考虑两个关键因素:
- 程序员对内存模型的使用。程序员希望内存模型易于理解,易于编程。程序员希望基于一个强内存模型来编写代码。
- 编译器和处理器对内存模型的实现。编译器和处理器希望内存模型对它们的束缚越少越好,这样它们就可以做尽可能多的优化来提高性能。编译器和处理器希望实现一个弱内存模型。
由于这两个因素互相矛盾,所以JSR-133专家组在设计JMM时的核心目标就是找到一个好的平衡点:一方面要为程序员提供足够强的内存可见性保证;另一方面,对编译器和处理器的限制要尽可能的放松。下面让我们看看JSR-133是如何实现这一目标的。
为了具体说明,请看前面提到过的计算圆面积的示例代码:
double pi = 3.14; //A
double r = 1.0; //B
double area = pi * r * r; //C
上面计算圆的面积的示例代码存在三个happens- before关系:
- A happens- before B;
- B happens- before C;
- A happens- before C;
由于A happens- before B,happens- before的定义会要求:A操作执行的结果要对B可见,且A操作的执行顺序排在B操作之前。 但是从程序语义的角度来说,对A和B做重排序即不会改变程序的执行结果,也还能提高程序的执行性能(允许这种重排序减少了对编译器和处理器优化的束缚)。也就是说,上面这3个happens- before关系中,虽然2和3是必需要的,但1是不必要的。因此,JMM把happens- before要求禁止的重排序分为了下面两类:
- 会改变程序执行结果的重排序。
- 不会改变程序执行结果的重排序。
JMM对这两种不同性质的重排序,采取了不同的策略:
- 对于会改变程序执行结果的重排序,JMM要求编译器和处理器必须禁止这种重排序。
- 对于不会改变程序执行结果的重排序,JMM对编译器和处理器不作要求(JMM允许这种重排序)。
下面是JMM的设计示意图:
从上图可以看出两点:
- JMM向程序员提供的happens- before规则能满足程序员的需求。JMM的happens- before规则不但简单易懂,而且也向程序员提供了足够强的内存可见性保证(有些内存可见性保证其实并不一定真实存在,比如上面的A happens- before B)。
- JMM对编译器和处理器的束缚已经尽可能的少。从上面的分析我们可以看出,JMM其实是在遵循一个基本原则:只要不改变程序的执行结果(指的是单线程程序和正确同步的多线程程序),编译器和处理器怎么优化都行。比如,如果编译器经过细致的分析后,认定一个锁只会被单个线程访问,那么这个锁可以被消除。再比如,如果编译器经过细致的分析后,认定一个volatile变量仅仅只会被单个线程访问,那么编译器可以把这个volatile变量当作一个普通变量来对待。这些优化既不会改变程序的执行结果,又能提高程序的执行效率。
JMM的内存可见性保证
Java程序的内存可见性保证按程序类型可以分为下列三类:
- 单线程程序。单线程程序不会出现内存可见性问题。编译器,runtime和处理器会共同确保单线程程序的执行结果与该程序在顺序一致性模型中的执行结果相同。
- 正确同步的多线程程序。正确同步的多线程程序的执行将具有顺序一致性(程序的执行结果与该程序在顺序一致性内存模型中的执行结果相同)。这是JMM关注的重点,JMM通过限制编译器和处理器的重排序来为程序员提供内存可见性保证。
- 未同步/未正确同步的多线程程序。JMM为它们提供了最小安全性保障:线程执行时读取到的值,要么是之前某个线程写入的值,要么是默认值(0,null,false)。
下图展示了这三类程序在JMM中与在顺序一致性内存模型中的执行结果的异同:
只要多线程程序是正确同步的,JMM保证该程序在任意的处理器平台上的执行结果,与该程序在顺序一致性内存模型中的执行结果一致。
JSR-133对旧内存模型的修补
JSR-133对JDK5之前的旧内存模型的修补主要有两个:
- 增强volatile的内存语义。旧内存模型允许volatile变量与普通变量重排序。JSR-133严格限制volatile变量与普通变量的重排序,使volatile的写-读和锁的释放-获取具有相同的内存语义。
- 增强final的内存语义。在旧内存模型中,多次读取同一个final变量的值可能会不相同。为此,JSR-133为final增加了两个重排序规则。现在,final具有了初始化安全性。
转载自并发编程网 – ifeve.com
更多内容请关注微信公众号【Java技术江湖】
一位阿里 Java 工程师的技术小站。作者黄小斜,专注 Java 相关技术:SSM、SpringBoot、MySQL、分布式、中间件、集群、Linux、网络、多线程,偶尔讲点Docker、ELK,同时也分享技术干货和学习经验,致力于Java全栈开发!(关注公众号后回复”Java“即可领取 Java基础、进阶、项目和架构师等免费学习资料,更有数据库、分布式、微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送作者原创的Java学习指南、Java程序员面试指南等干货资源)
Java并发指南6:Java内存模型JMM总结的更多相关文章
- Java并发指南2:深入理解Java内存模型JMM
本文转载自互联网,侵删 一:JMM基础与happens-before 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实 ...
- Java内存模型JMM 高并发原子性可见性有序性简介 多线程中篇(十)
JVM运行时内存结构回顾 在JVM相关的介绍中,有说到JAVA运行时的内存结构,简单回顾下 整体结构如下图所示,大致分为五大块 而对于方法区中的数据,是属于所有线程共享的数据结构 而对于虚拟机栈中数据 ...
- Java并发编程:Java内存模型JMM
简介 Java内存模型英文叫做(Java Memory Model),简称为JMM.Java虚拟机规范试图定义一种Java内存模型来屏蔽掉各种硬件和系统的内存访问差异,实现平台无关性. CPU和缓存一 ...
- Java内存模型JMM与可见性
Java内存模型JMM与可见性 标签(空格分隔): java 1 何为JMM JMM:通俗地讲,就是描述Java中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这 ...
- 多线程并发之java内存模型JMM
多线程概念的引入是人类又一次有效压寨计算机的体现,而且这也是非常有必要的,因为一般运算过程中涉及到数据的读取,例如从磁盘.其他系统.数据库等,CPU的运算速度与数据读取速度有一个严重的不平衡,期间如果 ...
- 全面理解Java内存模型(JMM)及volatile关键字(转载)
关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoad ...
- 全面理解Java内存模型(JMM)及volatile关键字(转)
原文地址:全面理解Java内存模型(JMM)及volatile关键字 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型( ...
- 什么是Java内存模型(JMM)
什么是java内存模型 缓存一致性问题 在现代计算机中,因为CPU的运算速度远大于内存的读写速度,因此为了不让CPU在计算的时候因为实时读取内存数据而影响运算速度,CPU会加入一层缓存,在运算之前缓存 ...
- 对多线程java内存模型JMM
多线程概念的引入体现了人类重新有效压力寨计算机.这是非常有必要的,由于所涉及的读数据的过程中的一般操作,如从磁盘.其他系统.数据库等,CPU计算速度和数据读取速度已经严重失衡.假设印刷过程中一个线程将 ...
随机推荐
- Golang内存管理
Golang 内存管理 原文链接[http://legendtkl.com/2017/04/02/golang-alloc/] Golang 的内存管理基于 tcmalloc,可以说起点挺高的.但是 ...
- nginx增加新模块
以gunzip这个模块为例,讲述一下,在nginx中如何安装新的模块 1.首先查看nginx已经安装了哪些模块. nginx –V 2.发现没有gunzip模块,安装 进入nginx的安装目录中,不是 ...
- Kubernetes 的 Client Libraries 的使用
说明 kubernetes 估计会成为 linux 一样的存在,client-go 是它的 go sdk,client-go/examples/ 给出了一些用例,但是数量比较少. api Resour ...
- MySQL授权(用户权限)
一.mysql查询与权限 (二)授权 用户管理: 设置用户密码 前期准备工作: 停止服务 将配置文件当中的skip-grant-tables删除掉 重启服务: 执行修改命令 查看用户状态(如果数据过多 ...
- Python更改pip源
Python更改pip源 pip源有以下 新版ubuntu要求使用https源,要注意.清华:https://pypi.tuna.tsinghua.edu.cn/simple阿里云:http://mi ...
- LoadRunner生成测试报告
loadrunner笔记(三):设置.运行场景和生成测试报告 //上一篇的代码有点问题,问题出在 web_reg_find()函数中,这个函数简单的说是搜索下一步操作的请求对象(html)页面中 ...
- Kostya the Sculptor(贪心
这题本来 想二分.想了很久很久,解决不了排序和二分的冲突. 用贪心吧.. 题意: 给你n个长方形,让你找出2个或1个长方体,使得他们拼接成的长方体的内接球半径最大(这是要求最短边越大越好)( ...
- python3 matplotlib
一.matplotlib数据可视化 1.https://matplotlib.org/ 2.figure图形窗口:figsize窗口大小,label轴标签:title标题:lim限制:plot绘图:s ...
- 三种方法给Vmware虚拟机占用空间清理瘦身
随着VMware虚拟机使用时间的增长,其所占用的空间也越来越大,本文来说说怎么给VMware虚拟机占用的空间进行瘦身. 方法一:VMware自带的清理磁盘这个方法是VMware自带,具有普适性,对快照 ...
- UEditor设置内容setContent()失效的解决方法
ueditor常见用法: https://blog.csdn.net/qq_31879707/article/details/54894735#UE.Editor:setContent() UEdit ...