JMM内存模型详解(一)
本文开始死磕JMM(Java内存模型)由于知识点较多,分来写
该文为JMM第一篇
技术往往是枯燥的,本文文字较多
1. JMM是什么?
其实JMM很好理解,我简单的解释一下,在Java多线程中我们经常会涉及到两个概念就是线程之间是如何通信和线程之间的同步,那什么是线程之间的通信呢,其实就是两个线程之间互相交换信息线程之间通信的方式共有两种:一种就是共享内存,和消息传递。在共享内存中的并发模型中线程是通过读取主内存的共享信息来进行隐性通信的。在消息传递通信中线程之间没有公共的状态,只能通过发送消息来进行显性通信。然而这只是线程通信,那么同步呢,同步就是在多线程的情况下有顺序的去执行。在共享内存中同步时显式进行的,在代码中我们必须要去指定方法需要同步执行比如说加同步锁等。在消息传递的并发模型中发送消息必须是在消接收之前,所以同步时隐式的。
2.为什么要涉及到线程并发通信
java内存模型其实可以说是Java并发内存模型,在Java中是采用的共享内存模型的方式,所以Java线程之间的通信是隐式进行的,对我们是完全透明的,如果你不了解通信机制的话会产生各种线程可见性的问题。其实在Java中所有的静态域,域和数组元素都存在堆内存中,堆内存在线程中是共享的一般我们都称之为共享变量,局部变量,方法定义参数和异常处理参数不会在线程中共享,所以不会存在线程可见性的问题。上面我就说过线程之间的通信是由JMM来进行控制的,JMM来决定了一个线程操作了共享变量后如何对另一个线程可见。从上面所说的概念来看的话,JMM定义了线程与主内存的关系。
3.JMM规定
其实这样做的原因就是Java是跨平台语言,在个操作系统中内存都有一定的差异性,这样久造成了并发不一致,所以JMM的作用就是用来屏蔽掉不同操作系统中的内存差异性来保持并发的一致性。同时JMM也规范了JVM如何与计算机内存进行交互。简单的来说JMM就是Java自己的一套协议来屏蔽掉各种硬件和操作系统的内存访问差异,实现平台一致性达到最终的"一次编写,到处运行",说了这么多,JMM到底是怎么控制的呢?然后如何通信的呢?我们继续往下看。
4.模型
JMM是一个抽象的概念,并不是真实的存在,它涵盖了缓冲区,寄存器以及其他硬件和编译器优化。
Java内存模型抽象图如下:

从上图可以看出每个线程都有一个本地内存,如果线程想要通信的话要执行一下步骤:
- A线程先把本地内存的值写入主内存
- B线程从主内存中去读取出A线程写的值
再看下面的这个图,表示了A如何向B发送消息

假设这时候有一个共享变量X默认值都是为0,那么线程A把X的值修改为1,这时候如何才能同步到B线程呢。
如果A线程把X修改成1之后,A线程会把X从A的本地内存中写入到主内存中,这样的话主内存的X就等于1了,这时候B线程就会去读取主内存的X变量,存入B的本地内存中,这样B线程的X变量值也就会变成了1。这样对吗。那现在如何通信我是知道了关键它究竟是如何来实现的,就是如何来实现通信的呢?
5.通信
上面所说的步骤其实就是实现了线程之间的通信,但是不要以为线程之间的通信就是这么简单的,其实在Java中JMM内存模型定义了八种操作来实现同步的细节。
- read 读取,作用于主内存把变量从主内存中读取到本本地内存。
- load 加载,主要作用本地内存,把从主内存中读取的变量加载到本地内存的变量副本中
- use 使用,主要作用本地内存,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。、
- assign 赋值 作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
- store 存储 作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
- write 写入 作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。
- lock 锁定 :作用于主内存的变量,把一个变量标识为一条线程独占状态。
- unlock 解锁:作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
所以看似简单的通信其实是这八种状态来实现的。
同时在Java内存模型中明确规定了要执行这些操作需要满足以下规则:
- 不允许read和load、store和write的操作单独出现。
- 不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中。
- 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中。
- 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。
- 一个变量在同一时刻只允许一条线程对其进行lock操作,lock和unlock必须成对出现
- 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值
- 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量。
- 对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)。
所以上面说的操作要严格执行。
目前写了这么多,下文预告:
LinkedHashMap源码分析
参考资料《深入Java内存模型》
JMM内存模型详解(一)的更多相关文章
- 云时代架构阅读笔记六——Java内存模型详解(二)
承接上文:云时代架构阅读笔记五——Java内存模型详解(一) 原子性.可见性.有序性 Java内存模型围绕着并发过程中如何处理原子性.可见性和有序性这三个特征来建立的,来逐个看一下: 1.原子性(At ...
- flink内存模型详解与案例
任务提交时的一些yarn设置(通用客户端模式) 指定并行度 -p 5 \ 指定yarn队列 -Dyarn.appl ...
- Java 内存模型详解
概述 Java的内存模型(Java Memory Model )简称JMM.首先应该明白,Java内存模型是一个规范,主要规定了以下两点: 规定了一个线程如何以及何时可以看到其他线程修改过后的共享变量 ...
- java内存模型详解
对于本篇文章,将从四个概念来介绍:内存模型基础,重排序,顺序一致性和happens-before 1.内存模型基础 在并发编程中,有两个关键问题:线程之间如何通信和如何同步.由此而引出了两种并发模型: ...
- 云时代架构阅读笔记五——Java内存模型详解(一)
什么是Java内存模型 Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model,JMM)来屏蔽掉各种硬件和操作系统的访问差异,以实现让Java程序在各种平台下都能达到一致 ...
- Java虚拟机:内存模型详解
版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 我们都知道,当虚拟机执行Java代码的时候,首先要把字节码文件加载到内存,那么这些类的信息都存放在内存中的哪个区域呢?当我们创建一个对象实 ...
- 深度历险:Redis 内存模型详解
https://mp.weixin.qq.com/s/Gp6Ur7omGY6ZqDWygU2meQ Redis 是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说 Redi ...
- JVM内存模型详解
内存模型 内存模型如下图所示 堆 堆是Java虚拟机所管理的内存最大一块.堆是所有线程共享的一块内存区域,在虚拟机启动时创建.此内存区域唯一的目的就是存放对象实例.所有的对象实例都在这里分配内存 Ja ...
- 02-java性能调优-JVM内存模型详解
JVM整体结构与内存模型之间的关系 JVM整体结构图如下: 先贴一个代码: package com.jvm.jvmCourse2; public class Math { public static ...
随机推荐
- 【CodeForces - 1167C 】News Distribution(并查集)
News Distribution 题意 大概就是分成几个小团体,给每个人用1 - n编号,当对某个人传播消息的时候,整个小团体就知道这个消息,输出 分别对1 - n编号的某个人传递消息时,有多少人知 ...
- poi解析excel文件报错
getFileMagic() only operates on streams which support mark(int) 使用 bis 解决 BufferedInputStream bis = ...
- Dock学习(一):容器介绍
一.什么是容器 1.容器是一种轻量级.可移植.自包含的软件打包技术,使应用程序可以在几乎任何地方以相同的方式运行.开发人员在自己的笔记本上创建并测试好的容器,无需任何修改就能够在生产系统的虚拟机.或物 ...
- 判断list集合不为空
在java开发中新手容易将判断一个list集合是否为空,只以If(list!=null)去判断,且容易和isEmpty()混淆,但是,list集合为空还是为null,是有区别的. 先看一下下面的例子, ...
- FJNUOJ 1002 画葫芦。。
画图就是..找..规..律 #include <iostream>using namespace std;int main(){ int T; cin>>T; while(T- ...
- 每日一问:Android 消息机制,我有必要再讲一次!
坚持原创日更,短平快的 Android 进阶系列,敬请直接在微信公众号搜索:nanchen,直接关注并设为星标,精彩不容错过. 我 17 年的 面试系列,曾写过一篇名为:Android 面试(五):探 ...
- MySql 命令(命令行)
1.连接Mysql 格式: mysql -h主机地址 -u用户名 -p用户密码1.连接到本机上的MYSQL. 首先打开DOS窗口,然后进入目录mysql\bin,再键入命令mysql -u root ...
- Redis(三)--- Redis的五大数据类型的底层实现
1.简介 Redis的五大数据类型也称五大数据对象:前面介绍过6大数据结构,Redis并没有直接使用这些结构来实现键值对数据库,而是使用这些结构构建了一个对象系统redisObject:这个对象系统包 ...
- docker跨主机通信扁平化网络的设计与实现
端口映射.ovs. fannel,weave 1.使用网桥实现跨主机容器连接 使用Open vSwitch实现跨主机容器连接
- containsObject 总是不含有,你会用吗
结论:containsObject:是在比较内存地址,即使两个对象内容完全一样,地址不同,那也是不同的.我个人认为这个方法应该叫是否存在同一个对象 (开始不知道这个知识,被坑,至少浪费了3个钟头,数组 ...