Java内存模型探秘
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内存模型探秘的更多相关文章
- JVM学习(3)——总结Java内存模型
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: 为什么学习Java的内存模式 缓存一致性问题 什么是内存模型 JMM(Java Memory Model)简 ...
- 浅析java内存模型--JMM(Java Memory Model)
在并发编程中,多个线程之间采取什么机制进行通信(信息交换),什么机制进行数据的同步? 在Java语言中,采用的是共享内存模型来实现多线程之间的信息交换和数据同步的. 线程之间通过共享程序公共的状态,通 ...
- JMM(java内存模型)
What is a memory model, anyway? In multiprocessorsystems, processors generally have one or more laye ...
- 《深入理解Java内存模型》读书总结
概要 文章是<深入理解Java内容模型>读书笔记,该书总共包括了3部分的知识. 第1部分,基本概念 包括"并发.同步.主内存.本地内存.重排序.内存屏障.happens befo ...
- Java内存模型深度解析:final--转
原文地址:http://www.codeceo.com/article/java-memory-6.html 与前面介绍的锁和Volatile相比较,对final域的读和写更像是普通的变量访问.对于f ...
- Java内存模型深度解析:volatile--转
原文地址:http://www.codeceo.com/article/java-memory-4.html Volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特 ...
- Java内存模型深度解析:顺序一致性--转
原文地址:http://www.codeceo.com/article/java-memory-3.html 数据竞争与顺序一致性保证 当程序未正确同步时,就会存在数据竞争.java内存模型规范对数据 ...
- Java内存模型深度解析:基础部分--转
原文地址:http://www.codeceo.com/article/java-memory-1.html 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何 ...
- 深入理解java内存模型系列文章
转载关于java内存模型的系列文章,写的非常好. 深入理解java内存模型(一)--基础 深入理解java内存模型(二)--重排序 深入理解java内存模型(三)--顺序一致性 深入理解java内存模 ...
随机推荐
- Jmeter学习之--dubbo接口测试
背景:公司的h5和APP都需要调用许多非http的服务,需要对服务的性能和自动化测试 工具:IDEA ,maven,Jmeter 参考文档: https://testerhome.com/topics ...
- opencart分类筛选逻辑修改为并且条件
opencart分类筛选模式默认是或的逻辑,满足条件1或条件2都展现出来,如果想要改成既满足条件1又满足条件2要怎么改呢?有一个插件可以实现,FixFilter OC2x,可以修改默认的筛选条件 1. ...
- zabbix源码安装实例
环境 系统 Centos7 zabbix版本 Zabbix 3.4.15 (revision 86739) zabbix源码安装 .tar.gz cd zab ...
- hystrix参数使用方法
hystrix+feign+ribbon,但是可能很多人都知道hystrix还有线程隔离,信号量隔离,等等各种参数配置,在这几就记录下hystrix的参数, 一.hystrix参数使用方法 通过注解@ ...
- Shell脚本创建的文件夹末尾有两个问号怎么回事?
原因:Linux系统的换行符是"\r\n",Windows上的换行符是"\n",Windows上编写shell脚本上传Linux,Linux无法正确识别&quo ...
- linux awk 常见字符串处理
awk指定输出列: awk '{print $0} file' #打印所有列awk '{print $1}' file #打印第一列 awk '{print $1, $3}' file #打印第一和第 ...
- Mac下创建证书失败
gdb调试运行出错,需要创建证书 按网上说的,到最后一步直接,按默认创建login类的证书 然后导出证书 再将这个证书导入到系统中
- UGUI背包系统
在Unity3d中,UGUI提供了Scroll Rect.Grid Layout Group.Mask这三个组件,下面就给大家介绍下如何用这个三个组件来实现滚动视图. 首先放置好背包的背景图 在矩形线 ...
- Linux下SVN创建新的项目
Linux下SVN创建新的项目 Linux环境下的SVN创建新的项目 一.前置条件: 1)有安装了linux系统的服务器,123.*.*.29 2)服务器上安装了svn,本人服务器的svn的数据安 ...
- Go 初体验 - 错误与异常处理
错误处理是学习任何编程语言都需要考虑的一个重要话题 go 内置的 error 接口是这样的: 先上代码: 输出: 释义: 我们首先定义9行的自定义错误类型 30行再实现 error 接口 34定义打开 ...