java内存模型是围绕着在并发过程中如何处理原子性,可见性跟有序性这三个问题来建立的。先看一下这三个特性:
1、原子性
由java内存模型来直接保证的原子性变量操作就是上文2中提到的8种基本操作,我们大致可以认为基本数据类型的读写是具备原子性的(long跟double不必太过在意)。如果需要一个更大范围的原子性保证,java内存模型还提供了lock跟unlock操作来满足这种需求。
原子性操作值相应的操作是单一不可分割的。例如对int变量count执行count++就不是原子操作。可以分解为3个操作:(1)读取count当前值;(2)count当前值跟1做加法运算;(3)将加完后的值赋给count变量。
多线程环境中,非原子操作可能会受其他线程干扰,比如上述例子,在执行第二个操作的时候,count的值可能已经被其它线程修改了。当然我们可以加锁或者synchronize来避免这种操作。synchronize可以实现原子性,实质是:通过该关键字所包括的临界区(Critical Section)的排他性保证在任何一个时刻只有一个线程能够执行临界区中的代码,这使得临界区中的代码代表了一个原子操作。这一点,大家基本都很清楚。但是,synchronized关键字所起到的另一个作用——保证内存的可见性(Memory Visibility),也是我们值得回顾的地方。
临界区:每个进程中访问临界资源的那段代码称为临界区(Critical Section)(临界资源是一次仅允许一个进程使用的共享资源)。每次只准许一个进程进入临界区,进入后不允许其他进程进入。不论是硬件临界资源,还是软件临界资源,多个进程必须互斥地对它进行访问。多个进程中涉及到同一个临界资源的临界区称为相关临界区
2、可见性
可见性指一个线程修改了共享变量的值,其它线程能够立即得知这个修改。比如volatile变量的修改,volatile关键字实现内存可见性的核心机制是:当一个线程修改了一个volatile修饰的变量的值时,该值会被写入主内存(即RAM)而不仅仅是当前线程所在的CPU的缓存区,而其他CPU的缓存区中存储的该变量的值也会因此而失效(从而得以更新为主内存中该变量的“新值”)。这就保证了其他线程访问该volatile修饰的变量时,总是可以获取到该变量的最新值。
除了volatile之外,java还有两个关键字能实现可见性:synchronized跟final。synchronize不做赘述,final的特殊规定如下:
与前面介绍的锁和volatile相比较,对final变量的读和写更像是普通的变量访问。对于final变量,编译器和处理器要遵守两个(分别对应读写)重排序规则:
1、在构造函数内对一个final变量的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
2、初次读一个包含final变量的对象的引用,与随后初次读这个final变量,这两个操作之间不能重排序。
写final变量的重排序规则
写final变量的重排序规则禁止把final变量的写重排序到构造函数之外。这个规则的实现包含下面2个方面:
1、JMM禁止编译器把final变量的写重排序到构造函数之外。
2、编译器会在final变量的写之后,构造函数return之前,插入一个StoreStore屏障。这个屏障禁止处理器把final变量的写重排序到构造函数之外 。
写final变量的重排序规则可以确保:在对象引用为任意线程可见之前,对象的final变量已经被正确初始化过了,而普通变量不具有这个保障
对于引用类型,写final变量的重排序规则对编译器和处理器增加了如下约束:
在构造函数内对一个final引用的对象的成员变量的写入,与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
读final变量的重排序规则
在一个线程中,初次读对象引用与初次读该对象包含的final变量,JMM禁止处理器重排序这两个操作(注意,这个规则仅仅针对处理器)。编译器会在读final域操作的前面插入一个LoadLoad屏障。初次读对象引用与初次读该对象包含的final变量,这两个操作之间存在间接依赖关系。由于编译器遵守间接依赖关系,因此编译器不会重排序这两个操作。
读final变量的重排序规则可以确保:在读一个对象的final变量之前,一定会先读包含这个final变量的对象的引用。在这个示例程序中,如果该引用不为null,那么引用对象的final变量一定已经被A线程初始化过了。
3、有序性
叫有序性其实有些容易误解,这个实际说的是重排序。线程内表现为串行,线程外表现为无序。就是说虽然执行过程中有重排序的优化问题,但从结果来看,单个线程执行结果并没有改变,但如果没有对公共资源加锁,多个线程并发执行的时候,会发现顺序是乱的,可能跟预期结果不同。
先行发生原则:
先行发生是指如果操作A先行发生于B,则在B操作之前,操作A产生的影响能被B观察到,影响包括修改了内存中共享变量的值,发生了消息调用了方法等。
例如:
//以下操作在线程A中执行
int i = 1;
//以下操作在线程B中执行
j = i;
//以下操作在线程C中执行
i = 2

  如果A先行发生于B,但B跟C没有先行发生关系,则B中j的值是不确定的。
  java中存在一些先行发生关系的操作,这些操作不需要同步就可以保证先行发生关系:
  1、程序次序规则。在一个线程内,书写在前面的代码先行发生于后面的。确切地说应该是,按照程序的控制流顺序,因为存在一些分支结构。
  2、Volatile变量规则。对一个volatile修饰的变量,对他的写操作先行发生于读操作。
  3、线程启动规则。Thread对象的start()方法先行发生于此线程的每一个动作。
  4、线程终止规则。线程的所有操作都先行发生于对此线程的终止检测。
  5、线程中断规则。对线程interrupt()方法的调用先行发生于被中断线程的代码所检测到的中断事件。
  6、对象终止规则。一个对象的初始化完成(构造函数之行结束)先行发生于发的finilize()方法的开始。
  7、传递性。A先行发生B,B先行发生C,那么,A先行发生C。
  8、管程锁定规则。一个unlock操作先行发生于后面对同一个锁的lock操作。

3、java内存模型特点的更多相关文章

  1. JVM学习(3)——总结Java内存模型

    俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: 为什么学习Java的内存模式 缓存一致性问题 什么是内存模型 JMM(Java Memory Model)简 ...

  2. 浅析java内存模型--JMM(Java Memory Model)

    在并发编程中,多个线程之间采取什么机制进行通信(信息交换),什么机制进行数据的同步? 在Java语言中,采用的是共享内存模型来实现多线程之间的信息交换和数据同步的. 线程之间通过共享程序公共的状态,通 ...

  3. JMM(java内存模型)

    What is a memory model, anyway? In multiprocessorsystems, processors generally have one or more laye ...

  4. 《深入理解Java内存模型》读书总结

    概要 文章是<深入理解Java内容模型>读书笔记,该书总共包括了3部分的知识. 第1部分,基本概念 包括"并发.同步.主内存.本地内存.重排序.内存屏障.happens befo ...

  5. Java内存模型深度解析:final--转

    原文地址:http://www.codeceo.com/article/java-memory-6.html 与前面介绍的锁和Volatile相比较,对final域的读和写更像是普通的变量访问.对于f ...

  6. Java内存模型深度解析:volatile--转

    原文地址:http://www.codeceo.com/article/java-memory-4.html Volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特 ...

  7. Java内存模型深度解析:顺序一致性--转

    原文地址:http://www.codeceo.com/article/java-memory-3.html 数据竞争与顺序一致性保证 当程序未正确同步时,就会存在数据竞争.java内存模型规范对数据 ...

  8. Java内存模型深度解析:基础部分--转

    原文地址:http://www.codeceo.com/article/java-memory-1.html 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何 ...

  9. 深入理解java内存模型系列文章

    转载关于java内存模型的系列文章,写的非常好. 深入理解java内存模型(一)--基础 深入理解java内存模型(二)--重排序 深入理解java内存模型(三)--顺序一致性 深入理解java内存模 ...

  10. Java内存模型深度解读

    Java内存模型规范了Java虚拟机与计算机内存是如何协同工作的.Java虚拟机是一个完整的计算机的一个模型,因此这个模型自然也包含一个内存模型——又称为Java内存模型. 如果你想设计表现良好的并发 ...

随机推荐

  1. logback-记录日志

      一:根节点<configuration>包含的属性: scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true. scanPeriod: 设置监测配 ...

  2. Springcloud踩坑记---使用feignclient远程调用服务404

    公司项目进行微服务改造,由之前的dubbo改用SpringCloud,微服务之间通过FeignClient进行调用,今天在测试的时候,eureka注册中心有相应的服务,但feignclient就是无法 ...

  3. 神经网络中的感受野(Receptive Field)

    在机器视觉领域的深度神经网络中有一个概念叫做感受野,用来表示网络内部的不同位置的神经元对原图像的感受范围的大小.神经元之所以无法对原始图像的所有信息进行感知,是因为在这些网络结构中普遍使用卷积层和po ...

  4. 十一、Node.js监听代码改动自动重启node插件supervisor

    我们慢慢地发现,每次我们稍微改变js代码都需要重启服务才能在浏览器显示新的效果,很麻烦,这里我们可以通过npm命令安装supervisoror插件,安装方法如下 之前章节我们知道安装了nodejs就会 ...

  5. [agc008f] Black Radius 树形dp

    Description ​ 给你一棵有NN个节点的树,节点编号为11到NN,所有边的长度都为11 ​ "全"对某些节点情有独钟,这些他喜欢的节点的信息会以一个长度为NN的字符串ss ...

  6. 我的csdn博客地址

    呆雁 持续的谦虚与努力 http://blog.csdn.net/u013539183

  7. Cboard Cube 实现Top X效果

    第一步,点击度量值指标的排序. 第二步,点击[过滤]图标,选择Filter 在窗口中输入需要Top X的数字即可. 预览效果如下: 版权声明:本文为博主原创文章,需要转载请注明出处. [置顶]Cboa ...

  8. redis锁

    1.引入依赖: <!--使用redis客户端需要用到的包 --> <dependency> <groupId>redis.clients</groupId&g ...

  9. office 2013 破解工具 及 软件下载

     win7/win8/win10  office2013 破解工具 下载地址: https://pan.baidu.com/s/1sZeJOCWq1fZ3KIOWvmrAQQ office2013  ...

  10. Vue 使用 axios post请求后台数据时 404

    今天遇到Vue 使用 axios post请求后台数据时 404 使用postman 就能获取到 网上找了大半天 终于找到了解决方法,传送门:https://www.jianshu.com/p/b10 ...