Java 内存模型- Java Memory Model
多线程越来越多的使用,使得我们需要对它的深入理解。那么就涉及到了Java内存模型JMM。JMM是JVM的一部分,JMM定义了一个线程修改了一个共享变量,其他线程什么时候或者如何看到这个变量,如何去访问共享变量。
咱们来看一张图(图片手绘的,字写的不好,见谅),JVM里边分为堆和栈,每一个线程都有一个线程栈,用于区分其他线程。

每个线程的入口是一个run方法,然后run方法开始调用其他方法。在方法中有两种数据类型,一种是原始类型,一种是引用类型。原始类型如( boolean, byte, short, char, int, long, float, double),这种其他线程根本看不到,只在线程中使用(存放在线程调用栈中)。另外一种是引用类型,引用类型比如原始类型的对象类型,比如(Boolean,Byte等),线程使用的时候会在堆中生成一个对象,并且将变量对其进行指向。每个线程使用此种变量的时候会自己创建一个变量(副本)并且指向,只有当前线程知道,其他线程看不到。那么引用对象的成员变量又分为基本类型和原始类型,并且一次进行创建副本并且指向。
在方法中用到static对象的实例的需要进行区别对待。因为在堆里边只有一个对象,所有线程对其进行引用。此时不是副本,需要注意。那么如果多线程对其惊醒操作的时候会出现值写的不对的,比如两个线程同时对对象里边的成员变量原始int类型count进行加1,如果count初始化的0的话,可能会出现结果为1的情况。
那么如果传过来的参数分为基本类型和引用类型呢?如果为基本类型,那么就是副本,如果是引用类型的话,就是原始对象的副本和副本指向,在这时需要注意,如果改了内存中对象的属相,那么随之这个对象会发生改变,但是对象的指向不会改变。
下面咱们通过代码才详细看一下,看之前首先看看图片。

咱们先进行简单的解释,一个对象,里边有两个方法,第一个方法只有一个原始类型变量,第二个方法有两个变量,一个是原始对象引用类型,另一个是静态对象实例的引用类型。这个图就是他们的内存结构图。下面咱们来看看代码:
package com.hqs.jmm; /**
*
* JMM对象
* @author qs.huang
*
*/
public class MyJMMObject implements Runnable{ @Override
public void run() {
methodOne();
} public void methodOne() {
int var1 = 0; //方法内部变量,原始类型
methodTwo();
} public void methodTwo(){
Integer var1 = new Integer(0); //方法内部变量,引用变量
MyReferenceObj var2 = MyReferenceObj.instance; //静态引用变量
} }
package com.hqs.jmm; /**
*
* 引用对象
* @author qs.huang
*
*/ public class MyReferenceObj {
public static MyReferenceObj instance = new MyReferenceObj();
public Integer intObj = new Integer(1);
public int intPrimary = 0;
//隐藏构造
private MyReferenceObj(){}
}
因为中的var1是方法1的局部变量,也是原始类型,每个用到它的地方,把它放到方法栈中。每一个线程都有自己的栈,所以每个一份。
方法2中的var1是一个对象类型的也是局部的,每个线程需要在堆里边创建一个对象,同时对它进行指向。
方法2中var2是一个静态对象实例的引用,所以再堆中只有一份,并且在加载的时候进行的实例化,因为对象中还有引用类型,所以产生了一个对intObj的引用,同时还有一个int原始类型存在堆里,跟随实例对象。
那么方法中如果有基本类型的数组呢?那数组会在堆中生成,然后对象只想堆中的数组对象,多线程的话,每个线程会生成自己所需要的副本,当方法调用完成后,该数组对象就会被收回。
下面咱们来看看CPU的硬件结构以便大家更理解JMM。看图:

目前的电脑一般都是2个或2个以上的CPU,每个CPU可能是多核的。那么每个CPU在同一时间就可以处理一个线程,多个CPU就可以同一时间执行多个线程。
每个CPU都有一个寄存器,CPU通过寄存器进行运算,那么在寄存器运算速度要高于在主内存进行运算。
每个CPU都附带一个缓存,用于将数据从主内存中读取到缓存数据中,然后再运算的时候放到寄存器里。CPU访问寄存器的速度是最快的,访问缓存的速度其次,最后是访问主内存的速度,当然缓存分为L1,L2 两个缓存,当然我画的没有那么好,不过不影响理解。CPU不会读取缓存中的所有数据,而是按照缓存line去进行有选择的读取。
当CPU执行完相关的运算并在适当的时候将结果刷到主内存RAM中,用于保存结果或让其他程序读取。咱们看一下JVM和CPU之间的关系。

因为CPU没有堆和栈,JVM的堆和栈会在CPU的主内存中,但是程序执行的时候,会将栈或堆中的线程读取到缓存和寄存器中进行运算,并且将计算的结果重新刷新到主内存RAM中。在这个时候因为有多CPU的原因,那么假如说一个CPU一个变量,那么两个并行的线程在执行的时候会有什么样的问题呢?
比如一个类中只有一个String state的成员变量,一个线程对其进行读取到CPU缓存中,然后将其设置为了'YES',并放回到缓存中;另一个线程没有看到这个值的更改,因为没有看到起更改。然后将其读取到CPU缓存中,然后设置为'YES'或'NO'。这个就是可见性问题,那么如何实现其他线程可见呢?Java有个关键字volatile,这个关键字可以使得操作不写入CPU缓存,直接从主内存读取,更改后直接重写到主缓存中。
比如这个类有个int count的成员变量,并且初始化值为0,向前面提到的,一个线程读取到这个count到CPU缓存中,另一个线程也把这个count读取到另一个CPU的线程中,那么两个线程放到寄存器计算,分别对其进行加1操作,这个时候都把结果刷新到缓存并且到主内存中。count的结果变为了1,这个不是大家想要的。因为每个线程对这个变量读取不可见,每个都用其副本进行操作。这个就是线程的竞态条件。那么怎么才能都保证这个变量的正确呢,就是使用同步,也就是使用synchronize关键字或者是锁来进行处理。也就是在同一时间只能有一个线程去处理这个字段或者方法,同时程序也是从主内存读取数据,然后计算完成后将程序写入到主内存中保证保证计算的有序处理。
这下同学们是否有了新的认识了呢?
如果有写的不对的地方希望告知~
Java 内存模型- Java Memory Model的更多相关文章
- Java内存模型(Java Memory Model,JMM)
今天简单聊聊什么叫做 Java 内存模型,不是 JVM 内存结构哦. JMM 是一个语言级别的内存模型,处理器的硬件模型是硬件级别,Java中的内存模型是内存可见性的基本保证.从而为我们 volati ...
- Java虚拟机12:Java内存模型
什么是Java内存模型 Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model,JMM)来屏蔽掉各种硬件和操作系统的访问差异,以实现让Java程序在各种平台下都能达到一致 ...
- 全面理解Java内存模型
尊重原创:http://blog.csdn.net/suifeng3051/article/details/52611310 Java内存模型即JavaMemory Model,简称JMM.JMM定义 ...
- 【JVM】JVM内存结构 VS Java内存模型 VS Java对象模型
原文:JVM内存结构 VS Java内存模型 VS Java对象模型 Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点.而且很多概念的名称看起来又那么相似,很多人会傻傻分不清 ...
- 全面理解Java内存模型(JMM)及volatile关键字(转载)
关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoad ...
- Java内存模型学习笔记
Java内存模型(JMM):描述了java程序中各种变量(线程共享变量)的范根规则,以及在JVM中将变量存储到内存和从内存中读取出变量这样的底层细节.共享变量就是指一个线程中的变量在其他线程中也是可见 ...
- 全面理解Java内存模型(JMM)及volatile关键字
[版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/72772461 出自[zejian ...
- 全面理解Java内存模型(JMM)
理解Java内存区域与Java内存模型Java内存区域 Java虚拟机在运行程序时会把其自动管理的内存划分为以上几个区域,每个区域都有的用途以及创建销毁的时机,其中蓝色部分代表的是所有线程共享的数据区 ...
- Java内存模型原理总结(转自51CTO)
转载地址:http://developer.51cto.com/art/201811/587220.htm [51CTO.com原创稿件]这篇文章主要介绍模型产生的问题背景,解决的问题,处理思路,相关 ...
随机推荐
- JAVA基础第十组(5道题)
46.[程序46] 题目:两个字符串连接程序 package com.niit.homework1; import java.util.Scanner; /** * @author: Annie * ...
- 微信小程序简单入门理解
简单的小程序示例结构: (一):理解小程序结构app.js,app.json,app.wxss ①app.js,app.json是小程序结构必要的部分,app.wxss可选择 ②app.js用于创建小 ...
- 201521145048《Java程序设计》第8周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 1.2 选做:收集你认为有用的代码片段 1.2 List<Map.Entry<String, In ...
- 201521123026《Java程序设计》第8周学习总结
1. 本章学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 2. 书面作业 Q1.1.List中指定元素的删除(题目4-1) 1.1 实验总结 答: 1.通过equals ...
- 201521123001《Java程序设计》第2周学习总结
本周学习总结 码云可以很方便地储存我们写好的代码,不用在写代码的时候担心没带U盘 Java中有许多已经写好的具有特定功能的一段独立小程序,不需要每一个函数都自己编写 Java的float型和C语言的不 ...
- 201521123032 《Java程序设计》第12周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 书面作业 将Student对象(属性:int id, String name,int age,doubl ...
- Hibernate的Configuration对象的configure()方法
Configuration configuration=new Configuration(); configuration.configure(); 在Hibernate底层实现configure( ...
- 基于jquery开发的UI框架整理分析
根据调查得知,现在市场中的UI框架差不多40个左右,不知大家都习惯性的用哪个框架,现在市场中有几款UI框架稍微的成熟一些,也是大家比较喜欢的一种UI框架,那应该是jQuery,有部分UI框架都是根据j ...
- 【Debian 8.8】Java 8 安装以及环境变量配置
事实上可以分为简单的三个步骤: 下载 JDK 压缩包 解压压缩包 配置环境变量 需要注意的是: 所有命令默认在 root 权限下进行! 演示环境是 Debian 8.8 64位 (阿里云学生机) 1. ...
- JVM菜鸟进阶高手之路九(解惑)
转载请注明原创出处,谢谢! 在第八系列最后有些疑惑的地方,后来还是在我坚持不懈不断打扰笨神,阿飞,ak大神等,终于解决了该问题.第八系列地址:http://www.jianshu.com/p/7f7c ...