要解决的问题

在硬件环境下,基于高速缓存的存储交互很好的解决了处理器与内存的速度之间的矛盾,但同时也带给计算机系统带来了复杂的缓存一致性问题。简单点说,在多处理器系统中,每个处理器都有自己的高速缓存,他们共享主内存,当在某个时刻,多个处理器的运算任务都涉及同一块内存区域,很有可能各自缓存中的运算结果不一致,此时若要执行将高速缓存中的数据同步回主内存的操作,那么问题就来了:系统以哪个缓存的数据为准? 由此便出现了解决缓存一致性的协议、规范。“内存模型”一词即指在特定的操作协议下,对特定的内存和高速缓存进行读写访问的过程抽象,不同架构的物理机器可以拥有自己的内存模型。JVM 也实现了这样的一套规范模型。

并发编程模型的分类

在并发编程中,需要解决的两个关键问题分别是:线程之间如何通信(以何种机制来交换信息)以及线程之间如何同步(不同线程之间操作发生的相对顺序机制)。

线程之间的通信机制有两种:共享内存和消息传递。在共享内存的并发模型里,线程之间共享程序的的公共状态,线程之间通过读-写内存中的公共状态来隐式进行通信;在消息传递的并发模型里,线程之间没有公共状态,必须通过明确的发送消息来显示通信。

在共享内存的并发编程模型里,同步就显得至关重要,必须显示指定。Java 的并发采用的就是共享内存模型,不同的线程之间无法访问对方本地内存中的变量,线程间变量值的传递均需要通过主内存来完成。

Java 内存模型的抽象

在 Java 中,所有实例域、静态域和数组元素存储在堆(主内存主要对应了堆中对象实例数据部分)中,堆内存在线程之间共享,因此他们也叫共享变量。而局部变量、方法参数和异常处理器参数是属于线程私有的,不会被共享。每个线程还有自己的工作内存,其中保存了被该线程使用到的变量的主内存副本拷贝,线程对共享变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读-写主内存中的共享变量。是不是和如上描述的硬件机制非常相似!实际上就是如此:主内存可以看出直接对应于物理硬件的内存,工作内存优先使用的是寄存器和高速缓存,因为程序运行时主要访问的是工作内存。

如上图所示,线程A和线程B通过共享变量实现了隐式通信,同时通过 JMM 控制实现多个线程之间的同步。解决了多核、多线程间数据的共享以及对内存操作的有序性的问题。

内存间交互操作细节

关于主内存与工作内存之间具体的交互协议,即一个变量如何从主内存拷贝到工作内存、又如何从工作内存同步回主内存的实现细节,JMM 中定义了 8 种操作来完成,且在虚拟机实现中都保证以下的每种操作都是原子的、不可再分的。

lock(锁定):作用于主内存的共享变量,把一个共享变量标识为一条线程独占的状态

read(读取):作用于主内存的共享变量,把一个共享变量的值从主内存传输到线程的工作内存的读缓冲区中,以便随后的 load 操作使用

load(载入):作用于工作内存的共享变量,把 read 操作从主内存中读取到读缓冲区中的变量值放入工作内存的共享变量副本中

use(使用):作于于工作内存的共享变量副本,把工作内存中的共享变量副本的值传递给执行引擎

assign(赋值):作用于工作内存的共享变量副本,把一个从执行引擎接收到的值赋给工作内存中的共享变量副本

store(存储):作用于工作内存的共享变量副本,把工作内存中的共享变量副本的值通过写缓冲区传送到主内存中,以便随后的 write 操作使用

write(写入):作用于主内存的共享变量,把 store 操作存储到写缓冲区的值放入主内存的共享变量中

unlock(解锁):作用于主内存的共享变量,把一个处于锁定状态的共享变量释放出来,释放后的变量才可以被其它线程锁定

上述 8 种原子操作还必须满足如下规则:

不允许 read 和 load、store 和 write 操作之一单独出现,即不允许一个变量从主内存读取了但工作内存不接受,或者从工作内存发起了回写但主内存不接受的情况出现。

read、load 和 store、write 此两类操作必须按顺序执行,但并非必须连续执行,也即在 read 和 load、store 和 write 操作之间是可以插入其他指令的

不允许一个线程丢弃它最近的 assign 操作,即变量在工作内存中改变了之后必须把该变化同步回主内存

不允许一个线程无原因地(没有发生任何 assign 操作)把数据从线程的工作内存同步回主内存

一个新的变量只能在主内存中“诞生”,不允许在工作内存中直接使用一个未被初始化(load 或 assign)的变量,即对一个变量实施 use 或 store 操作前必须先执行过 assign 或 load 操作

一个变量在同一时刻只允许一个一条线程对其进行 lock 操作,但 lock 操作可以被同一条线程重复执行多次,多次 lock 执行后,只有执行相同次数的 unlock 操作,变量才会被解锁

如果对一个变量执行 lock 操作,那将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行 load 或 assign 操作初始化变量的值

如果一个变量事先没有被 lock 操作锁定,那就不允许对它执行 unlock 操作,也不允许 unlock 一个被其它线程锁定的变量

对一个变量执行 unlock 前必须先把此变量同步回主内存中(执行 store、write 操作)

深入理解 Java 内存模型(一)- 内存模型介绍的更多相关文章

  1. 《深入理解 java 虚拟机》学习 -- 内存分配

    <深入理解 java 虚拟机>学习 -- 内存分配 1. Minor GC 和 Full GC 区别 概念: 新生代 GC(Minor GC):指发生在新生代的垃圾收集动作,因为 Java ...

  2. 深入理解Java虚拟机(一)——JVM内存模型

    文章目录 程序计数器 定义 作用 特点 Java虚拟机栈 定义 特点 本地方法栈 定义 Java堆 定义 特点 方法区 定义 特点 运行常量池 直接内存 总结 Java虚拟机的内存空间分为五个部分: ...

  3. 【深入理解Java虚拟机】自动内存管理机制——垃圾回收机制

      Java与C++之间有一堵有内存动态分配和垃圾收集技术所围成的"高墙",墙外面的人想进去,墙里面的人却想出来.C/C++程序员既拥有每一个对象的所有权,同时也担负着每一个对象生 ...

  4. 【深入理解Java虚拟机】自动内存管理机制——内存区域划分

      Java与C++之间有一堵有内存动态分配和垃圾收集技术所围成的"高墙",墙外面的人想进去,墙里面的人却想出来.C/C++程序员既拥有每一个对象的所有权,同时也担负着每一个对象生 ...

  5. 深入理解Java虚拟机(自动内存管理机制)

    文章首发于公众号:BaronTalk 书籍真的是常读常新,古人说「书读百遍其义自见」还是很有道理的.周志明老师的这本<深入理解 Java 虚拟机>我细读了不下三遍,每一次阅读都有新的收获, ...

  6. 深入理解Java虚拟机之JVM内存布局篇

    内存布局**** ​ JVM内存布局规定了Java在运行过程中内存申请.分配.管理的策略,保证了JVM的稳定高效运行.不同的JVM对于内存的划分方式和管理机制存在部分差异.结合JVM虚拟机规范,一起来 ...

  7. 深入理解JAVA虚拟机原理之内存分配策略(二)

    更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680 1.对象优先在Eden分配 大多情况,对象在新生代Eden区分配.当Eden区没 ...

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

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

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

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

  10. 【Todo】【转载】深入理解Java内存模型

    提纲挈领地说一下Java内存模型: 什么是Java内存模型 Java内存模型定义了一种多线程访问Java内存的规范.Java内存模型要完整讲不是这里几句话能说清楚的,我简单总结一下Java内存模型的几 ...

随机推荐

  1. 【Android】 导入项目报错的解决方案

    1.打项目的properties -->android 为其指一个运版本, 2.修改default properties 文件 ,改相应版本等级 3.选中项目,单击右键,选中properties ...

  2. iOS - 开发一套代码多个app展示不同图标和名称

    引言 公司项目重构之后,有了相对比较完善的开发体系,首先git分支分为日常.预发.生产三个主要分支,开发阶段都在日常(daily)分支下开相应功能的feature分支,开发完再合并. 我的iOS工程需 ...

  3. CSU1129 送货到家 【状压dp】

    哈哈发现这道题竟然没有题解,于是我决定写一份! 状压dp 题目: 懒惰的巫女Reimu因为各种原因在香霖堂的店主Rinnosuke那儿欠下了很多债,于是乎只好靠帮他在幻想乡中送货来偿还掉微不足道的一小 ...

  4. Scala学习笔记(1)-基本类型归纳

    1.小试牛刀 使用Scala自带的REPL shell(Read Evaluate Print Loop)学习和尝试Scala语言库,创建的变量在会话期间都是有效的. Ctrl+D可退出REPL sh ...

  5. Python 目录【持续更新中】

    Python 基础 字符编码 数据类型 文件处理 流程控制 练习题 函数,递归 匿名函数.内置函数 装饰器变形记 函数装饰器 递归 二分法 迭代器和生成器 协程函数 列表表达式 生成器表达式 异常处理 ...

  6. window10 telnet的启用

    (1) window+R打开运行窗口,输入control,如图: (2) 点击类别改成大图标: 如图所示: 然后点击程序和功能. (3) 然后依次点击:启用或者关闭windows功能->teln ...

  7. 【python-opencv】几何变换

    """几何变换-缩放""" img = cv.imread(r'pictures\family.jpg') ""&quo ...

  8. IO流(9)复制指定文件夹下指定文件到目的文件夹,并改名

    import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.File;import ja ...

  9. javaScript错误(一)Cannot call method 'addEventListener' of null

    Cannot call method 'addEventListener' of null 原因很简单,JavaScript代码中要引用到DOM对象,但是这个DOM对象在JavaScript执行后才会 ...

  10. idong常用js总结

    1.判断屏幕高度 $(document).ready(function() {    $("#left").height($(window).height());    $(&qu ...