一、何为“内存模型”

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

二、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. hiho week 136(二分+优先队列)

    题目链接:http://hihocoder.com/contest/hiho136/problem/1 题意:中文题诶- 思路:直接对缓存区长度二分就好了,注意计算当前长度的"延迟惩罚值&q ...

  2. 2014 Noip提高组 Day1

    P1328 生活大爆炸版石头剪刀布 [题目描述] 石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头.如果两个人出拳一样,则不分胜负.在<生活大爆炸>第二季第8 集中出现了一种石头 ...

  3. Linux上安装Apache服务器

    http://httpd.apache.org/download.cgi httpd-2.4.29.tar.gz #创建httpd用户 groupadd httpd useradd -g httpd ...

  4. PHP闭包和匿名函数

    概念 闭包和匿名函数在PHP5.3.0中被引入. 闭包 闭包是指创建时封装周围环境的函数.即使闭包所在的环境不存在了,闭包中封装的状态依然存在.这个概念很难理解,不过没关系,继续看下去就会明白了. 匿 ...

  5. 消息中间件之ActiveMQ(非原创)

    文章大纲 一.消息中间件基础知识二.ActiveMQ介绍三.ActiveMQ下载安装(Windows版本)四.Java操作ActiveMQ代码实战五.Spring整合ActiveMQ代码实战六.项目源 ...

  6. 利用system.reflection遍历一个类的变量成员

    假设有下面一个类,在程序中已初始化,如何获取里面的变量成员name,age,onduty及其值呢? public class Employee { public string name; public ...

  7. python进阶09 MySQL高级查询

    python进阶09 MySQL高级查询 一.筛选条件 # 比较运算符 # 等于:= 不等于:!= 或<> 大于:> 小于:< 大于等于>= 小于等于:<= #空: ...

  8. [bzoj 1758] 重建计划

    bzoj 1758 重建计划 题意: 给定一棵有边权的树和两个数 \(L, R (L\leq R)\),求一条简单路径,使得这条路径经过的边数在 \(L, R\) 之间且路径经过的边的边权的平均值最大 ...

  9. Vnc在Ubuntu14.04上的安装和配置 安装:

    安装: Ubuntu14.04 : sudo apt-get install vnc4server : sudo apt-get install xrdp iPad : 安装 vnc viewer 或 ...

  10. (转)linux mount (挂载命令)详解

    linux mount (挂载命令)详解 原文:http://tutu.spaces.eepw.com.cn/articles/article/item/70737 挂接命令(mount) 首先,介绍 ...