多线程越来越多的使用,使得我们需要对它的深入理解。那么就涉及到了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的更多相关文章

  1. Java内存模型(Java Memory Model,JMM)

    今天简单聊聊什么叫做 Java 内存模型,不是 JVM 内存结构哦. JMM 是一个语言级别的内存模型,处理器的硬件模型是硬件级别,Java中的内存模型是内存可见性的基本保证.从而为我们 volati ...

  2. Java虚拟机12:Java内存模型

    什么是Java内存模型 Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model,JMM)来屏蔽掉各种硬件和操作系统的访问差异,以实现让Java程序在各种平台下都能达到一致 ...

  3. 全面理解Java内存模型

    尊重原创:http://blog.csdn.net/suifeng3051/article/details/52611310 Java内存模型即JavaMemory Model,简称JMM.JMM定义 ...

  4. 【JVM】JVM内存结构 VS Java内存模型 VS Java对象模型

    原文:JVM内存结构 VS Java内存模型 VS Java对象模型 Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点.而且很多概念的名称看起来又那么相似,很多人会傻傻分不清 ...

  5. 全面理解Java内存模型(JMM)及volatile关键字(转载)

    关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoad ...

  6. Java内存模型学习笔记

    Java内存模型(JMM):描述了java程序中各种变量(线程共享变量)的范根规则,以及在JVM中将变量存储到内存和从内存中读取出变量这样的底层细节.共享变量就是指一个线程中的变量在其他线程中也是可见 ...

  7. 全面理解Java内存模型(JMM)及volatile关键字

    [版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/72772461 出自[zejian ...

  8. 全面理解Java内存模型(JMM)

    理解Java内存区域与Java内存模型Java内存区域 Java虚拟机在运行程序时会把其自动管理的内存划分为以上几个区域,每个区域都有的用途以及创建销毁的时机,其中蓝色部分代表的是所有线程共享的数据区 ...

  9. Java内存模型原理总结(转自51CTO)

    转载地址:http://developer.51cto.com/art/201811/587220.htm [51CTO.com原创稿件]这篇文章主要介绍模型产生的问题背景,解决的问题,处理思路,相关 ...

随机推荐

  1. SNS团队第三次站立会议(2017.04.24)

    一.当天站立式会议照片 本次会议主要内容:汇报工作进度,根据完成情况调整进度 二.每个人的工作 成员 今天已完成的工作 明天计划完成的工作 罗于婕 相关数据库文件建立起来  完善数据库文件 龚晓婷 研 ...

  2. 团队作业4——第一次项目冲刺(Alpha版本)4th day

    一.Daily Scrum Meeting照片 二.燃尽图 三.项目进展 计时功能已经完成,然后24点的代码如何在游戏界面与界面组件联系上正在进行. 四.困难与问题 1.在安卓框架与java代码的结合 ...

  3. 201521123034 《Java程序设计》第五周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 2. 书面作业 作业参考文件下载 代码阅读:Child压缩包内源代码 1.1 com.parent包中Child.java文件 ...

  4. Markdown格式范例

    一个例子: 例子开始 1. 本章学习总结 今天主要学习了三个知识点 封装 继承 多态 2. 书面作业 Q1. java HelloWorld命令中,HelloWorld这个参数是什么含义? 今天学了一 ...

  5. Spring第六篇【Spring AOP模块】

    前言 Spring的第五篇也算是AOP编程的开山篇了,主要讲解了代理模式-..本博文主要讲解Spring的AOP模块:注解方式和XML方式实现AOP编程.切入点表达式.. AOP的概述 Aop: as ...

  6. [02] Servlet获取请求和页面跳转

    1.Tomcat和Servlet的关系 之前提到过,Servlet是运行在Web容器里的,Tomcat作为容器的一种,在这里自然也要大概说说两者之间的大致关系. 首先,如上所述,Tomcat是Web应 ...

  7. 使用apache反向代理tomacat

    起源 在大部分的生产环境中,基本上使用的都是java程序,从而促进了各种应用程序中间件的产生,在这里大概有几种,tomcat作为最著名的开源servlet容器,jboss也是开源的,而且有管理界面,主 ...

  8. 1.Bootstrap-简介

    1.介绍 Bootstrap 是一个用于快速开发 Web 应用程序和网站的前端框架.Bootstrap 是基于 HTML.CSS.JAVASCRIPT 的. 2.HTML 模板 一个使用了 Boots ...

  9. 献身说法---修复bug时的一些小技巧

    最近,修复了项目当中的一些bug,觉着有些思路可以分享出来供大家借鉴. 场景一 开发环境中系统正常运行,测试环境中,部分机器未能正常运行. 解决过程:远程连接了测试环境中的机器,观察了系统的运行情况, ...

  10. 编码格式简介:ASCII码、ANSI、GBK、GB2312、GB18030和Unicode、UTF-8,BOM头

    编码格式简介:ASCII码.ANSI.GBK.GB2312.GB18030和Unicode.UTF-8,BOM头 二进制: 只有0和1. 十进制.十六进制.八进制: 计算机其实挺笨的,它只认识0101 ...