当我们在谈论JMM(Java memory model)的时候,我们在谈论些什么
前面几篇中,我们谈论了synchronized、final以及voilate的用法和底层实现,都绕不开一个话题-Java内存模型(java memory model,简称JMM)。Java内存模型是保证线程安全的基础,主要描述了程序中全序的同步动作在不同线程访问共享全局变量时所体现的原子性、可见性和有序性上的限制。
1、定义
维基百科定义:The Java memory model describes how threads in the Java programming language interact through memory. Together with the description of single-threaded execution of code, the memory model provides the semantics of the Java programming language
大意是说:Java内存模型描述了多个Java线程如何与内存交互,同单线程执行一样,在多线程场景下内存模型提供了一个合理正确Java编程语意。
JSR133规范由JSR133专家组开发,并首次在Java5.0中实现。规范详细准确的描述了多线程和内存交互语意,成为Java规范的一部分,改进原有Java语意中错误,模凌两可的部分,保证Java跨平台性。
JSR133中关于内存模型的定义如下:
给定一个程序和该程序的一串执行轨迹,内存模型描述了该执行轨迹是否是该程序 的一次合法执行。对于Java,内存模型检查执行轨迹中的每次读操作,然后根据特定规则,检验该读操作观察到的写是否合法。内存模型描述了某个程序的可能行为。JVM实现可以自由地生成想要的代码,只 要该程序所有最终执行产生的结果能通过内存模型进行预测。这为大量的代码转换 提供了充分的自由,包括动作(action)的重排序以及非必要的同步移除。
内存模型的一个高级、非正式的概述显示其是一组规则,规定了一个线程的写操作何时会对另一个线程可见。通俗地说,读操作r通常能看到任何写操作w写入的值,意味着w不是在r之后发生,且w看起来没有被另一个写操作w'覆盖掉(从 r 的角度看)。
在本内存模型规范中使用“读取(read)”这个词时,仅是指读取字段或数组元素的动作(action)。其它操作的语义,如读取数组的长度,执行受检转换以及虚方法 调用,都不会被数据争用直接影响到。JVM 实现有责任确保数据争用不会导致诸如返回错误的数组长度或调用虚方法导致段错误这类不正确的行为。
内存语义决定着程序中每个时刻能读到的值。每个单个线程中的动作(action)必须表现为被该线程的语义所控制,不包括读操作看到的值由内存模型决定的情况。当指的是这种情景时,我们说该程序遵守线程内(intra-thread)语义。
2、JMM近似模型
为了方便理解JMM,相比正式模型精确公式化的定义,JSR133提出了一种近似的模型-Happen-Before内存模型,这个描述是正式定义的必要非充分条件。首先需要解释一下同步动作的定义,同步动作指的是锁的加锁解锁,voilate对象读取,线程动作以及探测线程是否结束等。与同步动作对应的是同步边缘(synchronize-with边缘),同步边缘可以理解为同步动作间不可交叠屏障,包括以下几点:
- 某个管程m上的解锁动作synchronizes-with(同步与)所有后续在m上的锁定动作 (这里的后续是根据同步顺序定义的)。
- 对volatile变量v的写操作synchronizes-with所有后续任意线程对v的读操 作(这里的后续是根据同步顺序定义的)。
- 用于启动一个线程的动作synchronizes-with该新启动线程中的第一个动作。
- 线程T1的最后一个动作synchronizes-with线程T2中任一用于探测T1是否 终止的动作。T2可能通过调用 T1.isAlive()或者在T1上执行一个join动作来达到这个目的。
- 如果线程T1中断了线程T2,T1的中断操作synchronizes-with任意时刻任何其它线程(包括T2)用于确定T2是否被中断的操作。这可以通过抛出一个 InterruptedException或调用Thread.interrupted与Thread.isInterrupted来实现。
- 为每个变量写默认值(0,false或null)的动作synchronizes-with每个线程中的第一个动作。
- 虽然在对象分配之前就为该对象中的变量写入默认值看起来有些奇怪,从概念上看,程序启动创建对象时都带有默认的初始值。因此,任何对象的默认初始化操作 happens-before程序中的任意其它动作(除了写默认值的操作)。
- 调用对象的终结方法时,会隐式的读取该对象的引用。从一个对象的构造器末尾到该引用的读取之间存在一个happens-before边缘。注意,该对象的所有冻结操作happen-before前面那个happens-before边缘的起始点。
下面我们详细说一下这几条限制,第一条说明了管程(synchronized底层实现monitor)的加锁解锁具有同步关系,包括两点,同一个线程可以对已加锁对管程重复加锁,保证解锁次数一样即可,对于已经被加锁管程,线程阻塞并挂起。第二条volatile变量的写操作会在之后其他线程对volatile变量的读操作有即时的体现,一般通过内存一致性协议实现,一般写操作后会将对象的缓存写入内存,并使得其他核的缓存失效,从而读取内存中最新修改的值,这两条的实现在前面的文章中已有较详细的说明。第三条和第四条都是描述线程的启动在线程的第一个动作之前,线程最后一个动作在线程(探询终止???这个不是很理解)终止之前,不得重排序。第五条描述的是线程A对线程B的中断操作同步与线程B的中断检测动作,两个动作互斥发生。第六条第七条说明了,变量的默认值在线程中第一个动作或者其他操作之前,其实有时候编译器会优化变量默认值但赋值,只要该变量还未使用。第八条说明了对象的字段在构造器中的初始化赋值和对象引用返回,final字段保证初始化赋值在对象引用返回之前,而非final字段不保证。
所有的同步动作构成的全局顺序,称之为同步顺序,而synchronize-with边缘与程序顺序构成了Happen-before顺序,也就是Happen-before内存模型。Java内存模型是Happen-before内存模型的子集,因为Happen-before模型很多时候违背了因果关系,最为致命的弱点是“值的凭空出现”。关于Java内存模型的正式规范请参考JSR133第7章内容(我也看的云里雾里--!)
3、总结
总的来说,内存模型这个概念是由Java语言首先提出,在保证java语义完整性健壮性上起到了很大作用,C/C++以及其他语言在锁多线程并发模型上也参考这样的内存模型。理解JMM能让我们更加深刻的正确理解JAVA多线程并发执行中与内存交互的逻辑,从而编写出健壮高效的并发程序。
当我们在谈论JMM(Java memory model)的时候,我们在谈论些什么的更多相关文章
- java学习:JMM(java memory model)、volatile、synchronized、AtomicXXX理解
一.JMM(java memory model)内存模型 从网上淘来二张图: 上面这张图说的是,在多核CPU的系统中,每个核CPU自带高速缓存,然后计算机主板上也有一块内存-称为主内(即:内存条).工 ...
- 浅析java内存模型--JMM(Java Memory Model)
在并发编程中,多个线程之间采取什么机制进行通信(信息交换),什么机制进行数据的同步? 在Java语言中,采用的是共享内存模型来实现多线程之间的信息交换和数据同步的. 线程之间通过共享程序公共的状态,通 ...
- JMM(java Memory Model)到底是什么?
经历过很多面试大部分都会问一句: 你知道Java内存模型么? 然后我就pulapula的说一大堆什么堆呀,栈呀,GC呀什么的,这段时间把JVM虚拟机和多线程编程完整的学习了一遍,发现JMM和堆/栈这 ...
- JAVA内存模型(Java Memory Model ,JMM)
http://blog.csdn.net/hxpjava1/article/details/55189077 JVM有主内存(Main Memory)和工作内存(Working Memory),主内存 ...
- 死磕 java同步系列之JMM(Java Memory Model)
简介 Java内存模型是在硬件内存模型上的更高层的抽象,它屏蔽了各种硬件和操作系统访问的差异性,保证了Java程序在各种平台下对内存的访问都能达到一致的效果. 硬件内存模型 在正式讲解Java的内存模 ...
- 并发研究之Java内存模型(Java Memory Model)
Java内存模型JMM java内存模型定义 上一遍文章我们讲到了CPU缓存一致性以及内存屏障问题.那么Java作为一个跨平台的语言,它的实现要面对不同的底层硬件系统,设计一个中间层模型来屏蔽底层的硬 ...
- Java内存模型(Java Memory Model,JMM)
今天简单聊聊什么叫做 Java 内存模型,不是 JVM 内存结构哦. JMM 是一个语言级别的内存模型,处理器的硬件模型是硬件级别,Java中的内存模型是内存可见性的基本保证.从而为我们 volati ...
- Java 内存模型(Java Memory Model,JMM)
基本概念 JMM 本身是一种抽象的概念并不是真实存在,它描述的是一组规范,通过这组规范定义了程序的访问方式 JMM 同步规定 线程解锁前,必须把共享变量的值刷新回主内存 线程加锁前,必须读取主内存的最 ...
- 并发编程之java内存模型(Java Memory Model ,JMM)
一.图例 0.两个概念 Heap(堆):运行时的数据区,由垃圾回收负责,运行时分配内存(所以慢),对象存放在堆上 如果两个线程,同时调用同一个变量,怎两个线程都拥有,该对象的私有拷贝 (可以看一下,T ...
随机推荐
- Hive:添加、删除分区
添加分区: ', p_loctype='MHA'); 已经创建好的分区表: INFO : Loading partition {p_hour, p_city, p_loctype=MHA} INFO ...
- MyBatis(三):数据库查询结果不为空,但是使用MyBatis框架查询为空问题
1.这个问题主要和返回字段是否和实体类javabean中的字段是否一致导致的问题. 解决方案: sql语句 : select account_id as "accountId" a ...
- 原来你是这样的Promise
1. Promise简介 promise是异步编程的一种解决方案,它出现的初衷是为了解决回调地狱的问题. 打个比方,我需要: --(延迟1s)--> 输出1 --(延迟2s)--> 输出2 ...
- Java面试题1--关键字
1. final关键字有哪些用法? 修饰类.方法和变量. (1) final变量是只读的,不允许改变其引用,与static共用可声明常量.JVM会对final变量进行优化,比如常量折叠. (2) fi ...
- iframe交互(一)父页面自动高度
//父页面源码 <body style="border:1px solid red;width:200px;height:500px;" onload="IFram ...
- [LeetCode] Array Nesting 数组嵌套
A zero-indexed array A consisting of N different integers is given. The array contains all integers ...
- [LeetCode] Freedom Trail 自由之路
In the video game Fallout 4, the quest "Road to Freedom" requires players to reach a metal ...
- 系统引导修复 ---- Windows 和 Ubuntu
Windows: 1.准备windows相应镜像盘,u盘启动该系统 (以下均为windows10安装界面) 2.进入安装界面<语言,时间,键盘格式>,点击"下一步" 3 ...
- Struts2--二次提交
在Struts2中,使用token的方式来防止二次提交.并且在默认的拦截器栈中提供了两个默认拦截器Token Interceptor和Token Session Interceptor.必须要在for ...
- day 1——字典树练习
cojs 173. 词链 ★☆ 输入文件:link.in 输出文件:link.out 简单对比时间限制:1 s 内存限制:128 MB [问题描述]给定一个仅包含小写字母的英文单词表, ...