Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model,JMM)来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果。在此之前,主流程序怨言(如C/C++等)直接使用物理硬件(或者说操作系统的内存模型),因此,会由于不同的平台上内存模型差异,导致程序在一套平台上并发完成正常,而在另一套平台上并发访问却经常出错,因此经常需要针对不同的平台来编写程序。

Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。此处的变量(Variable)与java编程中所说的变量略有不同,它包括实例字段、静态字段和构成数组对象的元素,但是不包括局部变量和方法参数,因为后者是线程私有的,不会被共享,自然就不存在竞争问题。为了获得较好的执行效能,java内存模型并没有限制执行引擎使用特定的寄存器或缓存来和主内存交互,也没有限制即时编译器调整代码执行顺序这类权利。

Java内存模型规定了所有变量度存储在主内存(Main Memory, 对应于物理硬件的主内存,但此处仅是虚拟机内存的一部分)中。每条线程还有自己的工作内存(Working Memory, 对应于处理器的cache), 线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内中的变量。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递需要通过主内存来完成,线程、主内存、工作内存的交互关系如下图所示:

这里讲的主内存、工作内存与java堆、栈、方法区等并不是同一个层次的内存划分。如果一定要勉强对应起来,那从变量、主内存、工作内存的定义来看,主内存主要对应于java堆中对象的实例数据部分,而工作内存则对应于虚拟机栈中的部分区域;从更底层次来说,主内存就是硬件的内存,而为了获取更好的运行速度,虚拟机即硬件系统可能会让工作内存优先存储于寄存器和高速缓存cache中。

关于主内存与工作内存之间具体的交互协议,即一个变量如何从主内存拷贝到工作内存,如何从工作同步回主内存之类的实现细节,Java内存模型中定义了八种操作来完成:

  • lock(锁定):作用于主内存的变量它拔一个变量标识为一条线程独占的状态。
  • unlock(解锁):作用于主内存的变量,它把一个处于锁定状态变量释放出来,释放后的变量才可以被其他线程锁定。
  • read(读取):作用于主内的变量,把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。
  • load(载入):作用于工作内存的变量,把read读取操作从主内存中得到的变量值放入工作内存的变量拷贝中。
  • use(使用):作用于工作内存的变量,把工作内存中一个变量的值传递给java虚拟机执行引擎,每当虚拟机遇到一个需要使用到变量值的字节码指令时将会执行该操作。
  • 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内存模型还规定了在执行上述八种基本操作时必须满足如下规则:

  • 不允许read和load、store和write操作之一单独出现,即不允许一个变量从主内存读取可但工作内存不接受,或者从工作内存发起了回写但竹内薰不接受的情况出现。
  • 不允许一个线程丢弃它的最近的assign赋值操作,即工作内存变量值改变之后必须同步回主内存。
  • 只有发生过assign赋值操作的变量才需要从工作内存同步回主内存。
  • 一个新变量只能在主内存中产生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量,即一个变量在进行use和store操作之前,必须先执行过assgin和load操作
  • 一个变量在同一时刻只允许一条线程对其进行lock锁定操作,但是lock锁定可以被一条线程重复执行多次,多次执行lock之后,只有执行相同次数的unlock操作变量才会被解锁
  • 如果对一个变量执行lock锁定操作,将会清空工作内存中该变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值。
  • 如果一个变量事先没有被lock锁定,则不允许对这个变量进行unlock解锁操作,也不允许对一个被别的线程锁定的变量进行unlock解锁。
  • 一个变量进行unlock解锁操作之前,必须先把此变量同步回主内存中(执行store和write操作)。

-----深入理解Java虚拟机

Java多线程内存模型的更多相关文章

  1. JAVA多线程-内存模型、三大特性、线程池

    一.线程的三大特性 原子性.可见性.有序性 1)原子性,即一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行.原子性其实就是保证数据一致.线程安全一部分. 2)可见性,即 ...

  2. 并发编程之volatile与JMM多线程内存模型

    一.通过程序看现象 在开始为大家讲解Java 多线程缓存模型之前,我们先看下面的这一段代码.这段代码的逻辑很简单:主线程启动了两个子线程,一个线程1.一个线程2.线程1先执行,sleep睡眠2秒钟之后 ...

  3. java线程内存模型,线程、工作内存、主内存

    转自:http://rainyear.iteye.com/blog/1734311 java线程内存模型 线程.工作内存.主内存三者之间的交互关系图: key edeas 所有线程共享主内存 每个线程 ...

  4. Java虚拟机内存模型及垃圾回收监控调优

    Java虚拟机内存模型及垃圾回收监控调优 如果你想理解Java垃圾回收如果工作,那么理解JVM的内存模型就显的非常重要.今天我们就来看看JVM内存的各不同部分及如果监控和实现垃圾回收调优. JVM内存 ...

  5. [转载]《C++0x漫谈》系列之:多线程内存模型

    <C++0x漫谈>系列之:多线程内存模型 By 刘未鹏(pongba) 刘言|C++的罗浮宫(http://blog.csdn.net/pongba) <C++0x漫谈>系列导 ...

  6. Java虚拟机--内存模型与线程

    Java虚拟机--内存模型与线程 高速缓存:处理器要与内存交互,如读取.存储运算结果,而计算机的存储设备和处理器的运算速度差异巨大,所以加入一层读写速度和处理器接近的高速缓存来作为内存和处理器之间的缓 ...

  7. 全网最硬核 Java 新内存模型解析与实验单篇版(不断更新QA中)

    个人创作公约:本人声明创作的所有文章皆为自己原创,如果有参考任何文章的地方,会标注出来,如果有疏漏,欢迎大家批判.如果大家发现网上有抄袭本文章的,欢迎举报,并且积极向这个 github 仓库 提交 i ...

  8. Java 多线程共享模型之管程(上)

    主线程与守护线程 默认情况下,Java 进程需要等待所有线程都运行结束,才会结束.有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束. packag ...

  9. java String 内存模型

    关于java的内存模型,参照以下的一篇文章: https://isudox.com/2016/06/22/memory-model-of-string-in-java-language/

随机推荐

  1. SQL Server系统视图 [不定期更新]

    1.sys.objects:在数据库中创建的每个用户定义的架构作用域内的对象(如表.视图.约束.默认值.日志.规则存储过程等,但不包括DDL触发器)在该表中均对应一行. 列名 说明 name 对象名. ...

  2. 《JavaScript高级程序设计》笔记(1):<script>元素

    使用<script>元素内部的JavaScript代码将从上至下依此解释.在使用<script>嵌入的JavasCript代码时,代码任何地方不能出现"</sc ...

  3. php tpl 模板页面如和给js文件传参数

    有一个参数,服务器传给了php 模板页面,但模板包含的js需要得到这个参数值.如何处理: 一,在引入页面前加一句代码 <script type="text/javascript&quo ...

  4. mysql统计表的大小

    如下是sql语句: SELECT TABLE_NAME as name,DATA_LENGTH+INDEX_LENGTH as len,TABLE_ROWS as rows FROM informat ...

  5. TDirectory.IsEmpty判断指定目录是否为空

    使用函数: System.IOUtils.TDirectory.IsEmpty class function IsEmpty(const Path: string): Boolean; static; ...

  6. 关于mac上的homebrew

    首先它的安装指令并不难: /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/ ...

  7. crontab没有正确重定向导致磁盘inode节点空间满

    通常是发现磁盘没有满但是无法写入文件.提示“no space left on device”    用df -i 查看,应该会发现相应的分区是100%    一般都是crontab的job有问题,造成 ...

  8. Excel下拉框选项切换行颜色切换

    选择行颜色变化范围 开始-条件格式-新创建规则-"使用公式-" 录入:=$104B="确认" 点击"格式(F)-"->填充,选择填充颜 ...

  9. eclipse开发android程序常见问题解决办法

    1:R.java不自动更新或不见,gen文件夹里没生成文件. 解决办法: 这个一般是xml文件中有错误,如有英文大写,属性值错误等,解决了就会好. 如果错都排除了还没有生成或更新,那么可以点击proj ...

  10. KVO - 键值观察

    [基本概念] 键值观察是一种使对象获取其他对象的特定属性变化的通知机制.控制器层的绑定技术就是严重依赖键值观察获得模型层和控制器层的变化通知的.对于不依赖控制器层类的应用程序,键值观察提供了一种简化的 ...