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. xampp本地服务器+HBuilder配置php环境

    HBuilder配置PHP环境: 下载,运行HBuilder编辑器 打开右侧小窗口,点击设置图标—>设置web服务器—>外置web服务器                    输入你想要浏 ...

  2. 关于MVC刷新页面会两次请求页面的原因

    遇到这个奇葩的问题刚开始关注点全放在后台了 ,以为后台哪个地方存在问题,找了半天一无所获之后才开始把问题关注点移到前端,通过不断的注释前台代码, 终于发现了问题,没想到是这个js导致的问题 因为的Vi ...

  3. 用firebug 进行表单自定义提交

    在一些限制网页功能的场合,例如,防止复制内容,防止重复提交,限制操作的时间段/用户等,网页上一些按钮是灰化的(禁用的),这通常是通过设置元素的 disable属性来实现的.但在后台并没有做相应的功能限 ...

  4. C#ADO.NET基础二

    DataAdapter的使用,批量增删改 1.使用DataAdapter查询 private void Select2() { try { using (SQLiteConnection conn = ...

  5. React进阶篇(1) -- react-router4模块化

    本篇内容: 单一的路由无嵌套 多层嵌套路由 获取路径中的参数 按需加载 单一的路由无嵌套 routers.js import Home from 'components/Home'; import N ...

  6. JDK1.8源码阅读系列之二:LinkedList

    本篇随笔主要描述的是我阅读 LinkedList 源码期间的对于 LinkedList 的一些实现上的个人理解,有不对的地方,请指出- 先来看一下 LinkedList 的继承图: 由于 Abstra ...

  7. P2407 [SDOI2009]地图复原

    $ \color{#0066ff}{ 题目描述 }$ 很久以前,有一个传说中的"EWF"部族,他们世代生活在一个N×M的矩形大地上.虽然,生活的地区有高山.有沼泽,但通过勤劳勇敢, ...

  8. oracle导出clob 工具

    oracle导出clob 等大字段,用pl/sql 等无法导出,除了备份数据库,但是一般数据库中正式环境数据和开发环境中数据不一定完全一致或者合适导出,即使仅仅导出导入一个表,有时候也不方便或者业务上 ...

  9. nodejs post 数据到php $_POST["content"]接收不到的问题

    今天写了一段代码,要用到ajax调用php的接口,因为是https的,所以ajax不能跨域,于是使用nodejs调用接口,但是传输数据后 $_POST["content"]接收不到 ...

  10. 好用的在线HTML、CSS工具

    css3剪贴路径(clip-path)在线生成工具:http://tools.jb51.net/static/api/css3path/index.html json在线解析:https://www. ...