1.Java内存模型概述

  Java内存模型是一种抽象概念,不是真实存在的。主要定义了程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存取出变量这样的底层细节。注意:这里的变量仅包括实例字段、静态字段、构成数组对象的元素,但不包括局部变量与方法参数。因为后者是线程私有的,不会被共享,自然就不存在竞争问题。

2.主内存与工作内存

  Java内存模型规定了所有的变量都存储在主存中。每条线程还有自己的工作内存,工作内存中保存了该线程使用到的变量的主内存拷贝副本,线程对变量的操作都在工作内存中进行,不能直接读写主存中的变量。不同的线程之间也无法访问对方工作内存中的变量。线程间变量的传递需要通过主内存来完成。

线程、工作内存、主内存,之间的关系如下图:

内存间的交互操作

  即一个变量如何从工作内存同步到主内存,如何从主内存拷贝到工作内存之间的实现细节。

  Java内存模型定义了8中交互动作,虚拟机实现时必须保证每一种操作都是原子的、不可再分的。

  • lock(锁定):作用于主内存中的变量,他把一个变量标识为一个线程独占的状态。
  • unlock(解锁):作用于主内存中的变量,把一个变量从锁定状态释放出来,释放后才能被其他线程线程锁定。
  • read(读取):作用于主内存中的变量,把主存中的变量的值(注意与变量区分开)传输到线程的工作内存中。以便随后的load动作使用。
  • load(载入):作用于工作内存中的变量,把read操作从主存中读取到的变量的值放入工作内存中的变量副本中。
  • use(使用):作用于工作内存中的变量,把工作内存中的变量的值传递给执行引擎。每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。
  • assign(赋值):作用于工作内存中的变量,把一个执行引擎接收到的值赋给工作内存中的变量。每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store(存储):作用于工作内存中的变量,把工作内存中一个变量的值传送到主内存中,以便随后的write操作。
  • write(写入):作用于主内存中的变量,把store操作从工作内存中得到的值放入主内存的变量中。

如果把一个变量从主内存复制到工作内存,那么就要顺序的执行read、load操作。如果要把变量从工作内存同步到主内存中,就要顺序的执行store、write操作。注意:Java内存模型只要求上述两种操作必须按顺序执行,并没有保证是连续执行。也就是说,read和load、store和write之间是可插入其他指令的,例如对主内存中变量a和b进行访问时,可能出现顺序是read a、read b、load b、load  a。初次之外Java内存模型还规定了在执行上述8中操作时必须保证如下规则。

  • 不允许read、load、store、write,操作之一单独出现,即不允许一个变量从主存中读取了但是工作内存中不接受的情况,或者工作内存发起了回写操作但主内存不接受的情况。
  • 不允许一个线程放弃它的最近assign操作,即变量在工作内存中发生了改变必须同步回主内存中。
  • 不允许一个无原因的(即没有发生过任何assign赋值动作)把数据从工作内存同步到主内存。
  • 变量只能在主内存中诞生,不允许工作内存直接使用未初始化的变量。
  • 变量在同一时刻只允许一条线程对于lock操作,但lock操作可被同一线程执行多次,多次执行后只有执行相同次数的unlock,变量才会被解锁。
  • 对变量进行lock操作会清空工作内存中变量的值,使用此变量时会重新从主内存中进行获取。
  • 变量没被lock锁定就不允许对其unlock解锁。也不允许去解锁其他线程锁定住的变量。
  • 对变量进行unlock解锁前,必须把此变量同步到主内中。

对于volatile型变量的特殊操作:可以参考这篇博客<volatile关键字解析>

3.原子性、可见性与有序性

  • 原子性:是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其它线程干扰。
  • 可见性:指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。
  • 有序性:指程序执行的顺序按照代码的先后顺序执行。

4.先行发生原则

  先行发生是指Java内存模型中定义的两项操作之间的偏序关系。如果说操作A先行发生于操作B,也就是说发生操作B之前,操作A产生的影响能被操作B观察到。“影响”包括修改了共享变量的值、发送了消息、调用了方法等。

  下面举个栗子来促进下理解:

//线程A中执行

int i = 1;

//线程B中执行

int j = i;

//线程C中执行

i = 2;

  假设线程A优先于线程B执行,根据先行发生原则,待线程B执行后 j 的值一定等于1,不考虑C的情况。现在假设C介于A、B之间,没有先行发生原则,那么 j 的值我们就无法判断了。因为线程C对i的影响可能会被B观察到也可能不会,这时候线程B就存在读取到过期数据的风险,不具备多线程安全性。

Java模型中一些天然的先行发生关系

  • 程序次序规则:在一个线程内,按照程序代码顺序,书写在前面的操作先行发生于在后面的操作。换句话说就是控制流程顺序
  • 管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作。
  • volatile变量规则:volatile变量的写操作先行发生于后面对这个变量的读操作。
  • 线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作。
  • 线程终止规则:线程的所有操作都先行发生于对此线程的终止检测。我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段来检测线程是否终止。
  • 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。
  • 对象终结规则:一个对象的初始化完成先行发生于它的finalize()方法的开始。
  • 传递性:操作a先行发生于操作b,操作b先行发生于操作c,那么就可以得出操作a先行发生于操作c。

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内存模 ...

随机推荐

  1. 2016(4)数据库系统,ER模型,规范化理论,并发控制

    试题四(共25分) 阅读以下关于数据库设计的叙述,在答题纸上回答问题1至问题3. 某航空公司要开发一个订票信息处理系统,以方便各个代理商销售机票.开发小组经过设计,给出该系统的部分关系模式如下: 航班 ...

  2. pc端字体大小计算以及echart中字体大小计算

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  3. linux centos7磁盘格式化挂载之parted

    parted /dev/xvde mklabel gpt //划分为gpt分区 mkpart logical //创建逻辑分区 ext4 //开始大小 537G //结束大小 quit blkid l ...

  4. request内置对象

    request内置对象(JSP九大内置对象之一)简述:内置对象即已在容器内部创建完成,可以直接调用的对象.容器在接收到客户端的请求后会创建一个对象用于处理请求信息,该对象就是内置对象(属于“javax ...

  5. [elk]验证mapping字段数和数据字段数关系

    验证一个mapping下字段缺少或者超过 结论: 没有什么不可以. 1.如果数据字段不在mapping里,则动态会更新mapping. 2.数据字段数也可以小于mapping里字段数 创建一个mapp ...

  6. 关于sql server profiler 监控工具的使用

    勾选以下属性: 记录这个数据库访问磁盘的次数:

  7. 030-IHttpModule

    MyHttpModule.cs public class MyHttpModule : IHttpModule { public void Dispose() { } public void Init ...

  8. openssl源代码结构

    Openssl整个软件包主要包括三个主要的功能模块:密码算法库,SSL协议库,应用程序: 应用程序:主要包括密钥生成,证书管理,格式转换,数据加密,签名,SSL测试等. evp,对称算法,非对称算法, ...

  9. Task3

    姓名:蔡典 学号:1425052044 班级:信管142 兴趣爱好:电影,美剧,游戏 个人编程能力:较弱,没自己写过代码 码云账号:18809188@qq.com 直接搜索码云然后进入官网输入基本信息 ...

  10. Linux系统安装Docker

    1.安装Linux系统 2.查看centos内核版本.Docker要求centos的内核版本必须高于3.10.使用命令:uname -r 查看内核版本.如果低于3.10,使用命令:yum update ...