一、何为“内存模型”

  内存模型描述了程序中各个变量(实例域、静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存中取出变量这样的底层细节,对象最终是存储在内存里面的,但是编译器、运行库、处理器或者系统缓存可以有特权在变量指定内存位置存储或者取出变量的值。

二、JMM(Java Memory Model)即Java内存模型的作用

  1. JMM的最初目的是为了能够支持多线程程序。JMM使得每一个线程就像运行在不同的机器、不同的CPU或者本身就不同的线程上一样;
  2. JMM定义了Java语言针对内存的一系列相关规则。对于CPU本身而言,一个CPU不能直接访问其它CPU的寄存器,因此JMM必须通过某种定义规则来使得线程和线程在工作内存中进行相互调用,从而实现一个CPU对其它CPU、或者说一个线程对其它线程的内存中资源的访问;
  3. 虽然JMM设计之初是为了能够更好地支持多线程,但是JMM的应用和实现并不局限于多处理器,对于单CPU的系统而言,在JVM编译器编译Java程序的时候,以及运行时执行该程序的时候,这种规则也是有效的;
  4. JMM定义了线程与主存之间的抽象关系:每个线程可以被抽象为一块工作内存,程序中所有的共享变量都在主存中定义并存储,工作内存不能直接使用主存中的共享变量,如果要使用,工作内存必须对主存中的共享变量进行读取和拷贝,然后对拷贝过来的变量副本进行操作,最后将操作后的变量结果回写到主存中。大多数JMM规则在实现的时候,必须保证主存和工作内存之间进行通信,而且不能违反内存模型本身的结构。这是在设计语言的时候必须考虑到的针对内存的一种设计方法。

  作为Java程序员,我们需要知道的是,Java对内存的管理不需要人为操作,因为Java本身就拥有了一套自动的内存管理策略,这是Java相对与其它一些语言在进行内存管理上具备的一种优势。

三、线程间通信机制

  在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。

  1. 共享内存。线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信;
  2. 消息传递。线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信。

  Java在实现线程间通信时采用的是共享内存的方式,因而Java线程之间的通信总是隐式的,整个通信过程对程序员完全透明。如果我们在编写多线程程序的时候不理解这种隐式的通信机制,很可能会遇到各种奇怪的并发问题。

四、主存与工作内存

  上面我们将每个单独的线程抽象为一块工作内存,主存与线程之间的关系也就被抽象成了主存与工作内存的关系,这种关系用图可表示为:

  JMM定义了8中主存与工作内存之间的操作:

  1. lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态;
  2. unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定;
  3. read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用;
  4. load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中;
  5. use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作;
  6. assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作;
  7. store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作;
  8. write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。

  Java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行。也就是read和load之间,store和write之间是可以插入其他指令的,如对主内存中的变量a、b进行访问时,可能的顺序是read a,read b,load b, load a。

  JMM还规定了在执行上述八种基本操作时,必须满足如下规则:

  1. 不允许read和load、store和write操作之一单独出现;
  2. 不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中;
  3. 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中;
  4. 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作;
  5. 一个变量在同一时刻只允许一条线程对其进行lock操作,lock和unlock必须成对出现;
  6. 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值;
  7. 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量;
  8. 对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)。

五、关于重排序

  在Java程序的执行过程中,编译器和处理器会通过对指令进行重排序来优化程序的执行效率。重排序分为三种:

  1.编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序;

  2.指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序;

  3.内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

  从java源代码到最终实际执行的指令序列,会分别经历上面三种重排序。

JAVA多线程编程——JAVA内存模型的更多相关文章

  1. Java并发编程、内存模型与Volatile

    http://www.importnew.com/24082.html  volatile关键字 http://www.importnew.com/16142.html  ConcurrentHash ...

  2. Java多线程中的内存模型

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6536131.html  一:现代计算机的高速缓存 在计算机组成原理中讲到,现代计算机为了匹配 计算机存储设备的 ...

  3. [心得笔记]Java多线程中的内存模型

    一:现代计算机的高速缓存 在计算机组成原理中讲到,现代计算机为了匹配 计算机存储设备的读写速度 与  处理器运算速度,在CPU和内存设备之间加入了一个名为Cache的高速缓存设备来作为缓冲:将运算需要 ...

  4. 初识Java多线程编程

    Java 多线程编程 Java给多线程编程提供了内置的支持.一个多线程程序包含两个或多个能并发运行的部分.程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径. 多线程是多任务的一种特别 ...

  5. Java - 32 Java 多线程编程

    Java 多线程编程 Java给多线程编程提供了内置的支持.一个多线程程序包含两个或多个能并发运行的部分.程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径. 多线程是多任务的一种特别 ...

  6. Java-Runoob-高级教程: Java 多线程编程

    ylbtech-Java-Runoob-高级教程: Java 多线程编程 1.返回顶部 1. Java 多线程编程 Java 给多线程编程提供了内置的支持. 一条线程指的是进程中一个单一顺序的控制流, ...

  7. Java 学习(19):Java 多线程编程

    Java 多线程编程 Java 给多线程编程提供了内置的支持.一个多线程程序包含两个或多个能并发运行的部分.程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径. 多线程是多任务的一种特 ...

  8. Java并发编程-Java内存模型

    JVM内存结构与Java内存模型经常会混淆在一起,本文将对Java内存模型进行详细说明,并解释Java内存模型在线程通信方面起到的作用. 我们常说的JVM内存模式指的是JVM的内存分区:而Java内存 ...

  9. Java多线程编程实战02:多线程编程模型

    多线程编程模型 线程安全名词 串行.并发和并行 串行:一个人,将任务一个一个完成 并发:一个人,有策略地同时做多件事情 并行:多个人,每人做一个事情 竞态 名词 竞态:计算结果的正确性与时间有关的现象 ...

随机推荐

  1. HDU 1532 Drainage Ditches (网络流)

    A - Drainage Ditches Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64 ...

  2. ES5.X相关API和技巧汇总

    https://blog.csdn.net/laoyang360/article/details/77412668

  3. NFS资料

      Linux NFS服务器的安装与配置 http://www.cnblogs.com/mchina/archive/2013/01/03/2840040.html Linux NFS服务器的安装与配 ...

  4. 微信小程序开发之下拉菜单

    实现功能:点击维保人员,调出下拉菜单.选择子菜单时,显示右边的图标表示选中,并进行赋值并进行搜索筛选 Wxml: <view class="dtclass" bindtap= ...

  5. 建立JNI层的本地对象,并与JAVA层交互方法

    最近在做OpenCV4Android开发的时候,需要从摄像头捕获多个图片,传值给本地对象,每次捕获到图片都更新数据,最后将计算得到的数据传给JAVA层,并在界面中显示.经过摸索和调试后,找到了通过本地 ...

  6. poj 2407 欧拉函数裸题

    http://poj.org/problem?id=2407 题意:多组数据,每次输入一个数 ,求这个数的欧拉函数 int euler_phi(int n){//单个欧拉函数 int m=(int)s ...

  7. Python面向对象之结构与成员

    1.面向对象结构分析: ----面相对象整体大致分为两块区域: --------第一部分:静态字段(静态变量)部分 --------第二部分:方法部分 --每个大区域可以分为多个小部分: class ...

  8. Codeforces 1168A(二分check)

    关键是check.要注意到其实有了mid以后每个位置都是独立的,它能从哪走到哪是固定了的,只要从左到右尽量贪心压着最小值即可. #include <cstdio> const int ma ...

  9. 解读 iostat -mxd 1

    #### for AWR 报告 : 建议如下: 不能随便调整db_file_multiblock_read_count 值, 取同样时间段的AWR 报告 02:00 ~ 05:00,所以db_file ...

  10. SpringBoot---Web开发

    一.概述 1.SpringBoot提供了spring-boot-starter-web为 web开发 予以支持: 2.spring-boot-starter-web提供了 内嵌的Tomcat 以及 S ...