一、硬件内存架构

一个现代计算机通常由两个或者多个CPU。其中一些CPU还有多核。每个CPU在某一时刻运行一个线程是没有问题的。如果你的Java程序是多线程的,在你的Java程序中每个CPU上一个线程可能同时(并发)执行。

当一个CPU需要读取主存时,它会将主存的部分读到CPU缓存中。它甚至可能将缓存中的部分内容读到它的内部寄存器中,然后在寄存器中执行操作。

当CPU需要将结果写回到主存中去时,它会将内部寄存器的值刷新到缓存中,然后在某个时间点将值刷新回主存。

二、并发编程的问题

并发编程,为了保证数据的安全,需要满足以下三个特性:

原子性:在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。(处理器优化)

  原子性问题:线程在执行一个读改写操作时,在执行完读改之后,时间片耗完,就会被要求放弃CPU,并等待重新调度。此时另一个线程对同一个变量执行读改写操作就会出现问题。这种情况下,读改写就不是一个原子操作。

i = 0;      // 基本数据类型的变量和赋值操作都是原子性操作
j = i ; // 包含了两个操作:读取i,将i值赋值给j
i++; // 包含了三个操作:读取i值、i + 1 、将+1结果赋值给i
i = j + 1; // 包含了三个操作:读取j值、j + 1 、将+1结果赋值给i

  在单线程环境下我们可以认为整个步骤都是原子性操作。但是在多线程环境下则不同,Java只保证了基本数据类型的变量和赋值操作才是原子性的。要想在多线程环境下保证原子性,则可以通过锁、synchronized来确保。

可见性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。(缓存一致性问题)

有序性:程序执行的顺序按照代码的先后顺序执行。(指令重排)

内存模型通过限制处理器优化和使用内存屏障,来保证共享内存的正确性(可见性、有序性、原子性)。

Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。

JMM还通过volatilesynchronizedfinalconcurren包等实现原子性、有序性、可见性。

三、Java内存模型(JMM)

共享变量:堆内存在线程之间共享,存储在堆内存中所有实例域、静态域和数组元素共享变量

  (局部变量,方法定义参数、异常处理器参数不会在线程之间共享,不会有内存可见性问题,不受内存模型的影响)

JMM定义了线程和主内存之间的抽象关系:

  1)线程之间的共享变量存储在主内存(main memory)中

  2)每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程用以读/写共享变量的副本

  3)本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化

线程A与线程B通信:

  1)线程A把本地内存A中更新过的共享变量刷新到主内存中去

  2)线程B到主内存中去读取线程A之前已更新过的共享变量

JMM通过控制主内存与每个线程的本地内存之间的交互,提供内存可见性保证

JMM的设计

1)常见的处理器内存模型比JMM要弱,java编译器在生成字节码时,会在执行指令序列的适当位置插入内存屏障来限制处理器的重排序。

2)由于各种处理器内存模型的强弱并不相同,为了在不同的处理器平台向程序员展示一个一致的内存模型,JMM在不同的处理器中需要插入的内存屏障的数量和种类也不相同。

程序员希望:强内存模型编程,易于理解,易于编程

编译器和处理器希望:弱内存模型,内存模型对它们的束缚越少越好,以提高性能

JMM时的核心目标就是找到一个好的平衡点:一方面要为程序员提供足够强的内存可见性保证;另一方面,对编译器和处理器的限制要尽可能的放松。

JMM把happens- before要求禁止的重排序分为了下面两类:

1)会改变程序执行结果的重排序,JMM要求编译器和处理器必须禁止这种重排序。

2)不会改变程序执行结果的重排序,JMM对编译器和处理器不作要求(JMM允许这种重排序)。

  只要不改变程序的执行结果(指的是单线程程序和正确同步的多线程程序),编译器和处理器怎么优化都行。

  比如,如果编译器经过细致的分析后,认定一个锁只会被单个线程访问,那么这个锁可以被消除。

  再比如,如果编译器经过细致的分析后,认定一个volatile变量仅仅只会被单个线程访问,那么编译器可以把这个volatile变量当作一个普通变量来对待。

  这些优化既不会改变程序的执行结果,又能提高程序的执行效率。

四、顺序一致性内存模型

顺序一致性内存模型是一个被计算机科学家理想化了的理论参考模型,它为程序员提供了极强的内存可见性保证(JMM没有顺序一致性内存模型保证)

特性:

  • 一个线程中的所有操作必须按照程序的顺序来执行。
  • (不管程序是否同步)所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须原子执行且立刻对所有线程可见。

视图信息:

1.顺序一致性模型有一个单一的全局内存

2.在任意时间点最多只能有一个线程可以连接到内存

3.每一个线程必须按程序的顺序来执行内存读/写操作

举例:

线程A:A1->A2->A3  线程B:B1->B2->B3  并发执行

正确同步:

两个线程没有做同步:

可以看出:

1.每个线程内部执行顺序 都是按照程序的顺序来执行

2.所有线程都只能看到一个一致的整体执行顺序(原因:顺序一致性内存模型中的每个操作必须立即对任意线程可见)

顺序一致性模型与JMM区别:

  顺序一致性模型保证单线程内的操作会按程序的顺序执行,JMM不保证单线程内的操作会按程序的顺序执行(遵守as-if-serial语义)

  顺序一致性模型保证所有线程只能看到一致的操作执行顺序,而JMM不保证所有线程能看到一致的操作执行顺序

JMM在具体实现上的基本方针:在不改变(正确同步的)程序执行结果的前提下,尽可能的为编译器和处理器的优化打开方便之门。

正确同步,JMM保证程序的执行结果将与该程序在顺序一致性模型中的执行结果相同(但不保证执行顺序)

假设A线程执行writer()方法后,B线程执行reader()方法

五、处理器内存模型

如果完全按照顺序一致性模型来实现,那么很多的处理器和编译器优化都要被禁止,这对执行性能将会有很大的影响。

根据对不同类型读/写操作组合的执行顺序的放松,可以把常见处理器的内存模型划分为下面几种类型:

  1. 放松程序中写-读操作的顺序,由此产生了total store ordering内存模型(简称为TSO)。
  2. 在前面1的基础上,继续放松程序中写-写操作的顺序,由此产生了partial store order 内存模型(简称为PSO)。
  3. 在前面1和2的基础上,继续放松程序中读-写和读-读操作的顺序,由此产生了relaxed memory order内存模型(简称为RMO)和PowerPC内存模型。

注意,这里处理器对读/写操作的放松,是以两个操作之间不存在数据依赖性为前提的(因为处理器要遵守as-if-serial语义,处理器不会对存在数据依赖性的两个内存操作做重排序)。

从上到下,模型由强变弱。越是追求性能的处理器,内存模型设计的会越弱。因为这些处理器希望内存模型对它们的束缚越少越好,这样它们就可以做尽可能多的优化来提高性能。

JMM,处理器内存模型,顺序一致性内存模型之间的关系

JMM是一个语言级的内存模型,处理器内存模型是硬件级的内存模型,顺序一致性内存模型是一个理论参考模型。

语言内存模型,处理器内存模型和顺序一致性内存模型的强弱对比示意图:

内存模型越强,越容易保证内存可见性,易编程性就越好。但是重排序就会越少,执行效率就越低。

重排序 :Java并发(三):重排序

happens-before:Java并发(四):happens-before

volatile:Java并发(六):volatile的实现原理

Final:Java并发(十九):final实现原理

参考资料:

《成神之路-基础篇》JVM——Java内存模型

细说Java多线程之内存可见性

Java内存模型FAQ

深入理解Java内存模型

Java并发(二):Java内存模型的更多相关文章

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

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

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

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

  3. java中JVM虚拟机内存模型详细说明

    java中JVM虚拟机内存模型详细说明 2012-12-12 18:36:03|  分类: JAVA |  标签:java  jvm  堆内存  虚拟机  |举报|字号 订阅     JVM的内部结构 ...

  4. Java虚拟机学习 - 体系结构 内存模型

    一:Java技术体系模块图 二:JVM内存区域模型 1.方法区 也称"永久代” .“非堆”, 它用于存储虚拟机加载的类信息.常量.静态变量.是各个线程共享的内存区域.默认最小值为16MB,最 ...

  5. Java虚拟机学习 - 体系结构 内存模型(1)

    一:Java技术体系模块图 二:JVM内存区域模型 1.方法区 也称"永久代" ."非堆",  它用于存储虚拟机加载的类信息.常量.静态变量.是各个线程共享的内 ...

  6. Java虚拟机学习 - 体系结构 内存模型(转载)

    一:Java技术体系模块图 二:JVM内存区域模型 1.方法区 也称"永久代” .“非堆”,  它用于存储虚拟机加载的类信息.常量.静态变量.是各个线程共享的内存区域.默认最小值为16MB, ...

  7. 【java虚拟机】jvm内存模型

    作者:pengjunlee原文链接:https://blog.csdn.net/pengjunlee/article/details/71909239 目录 一.运行时数据区域 1.程序计数器 2.J ...

  8. Java:JVM的内存模型

    JVM内存模型 JVM内存模型可以分为两个部分,如下图所示,堆和方法区是所有线程共有的,而虚拟机栈,本地方法栈和程序计数器则是线程私有的.   1. 堆(Heap) 堆内存是所有线程共有的,可以分为两 ...

  9. 深入理解Java虚拟机(一)——JVM内存模型

    文章目录 程序计数器 定义 作用 特点 Java虚拟机栈 定义 特点 本地方法栈 定义 Java堆 定义 特点 方法区 定义 特点 运行常量池 直接内存 总结 Java虚拟机的内存空间分为五个部分: ...

  10. JVM 系列(二)内存模型

    02 JVM 系列(二)内存模型 一.JVM 内存区域 JVM 会将 Java 进程所管理的内存划分为若干不同的数据区域.这些区域有各自的用途.创建/销毁时间: 一. 线程私有区域 线程私有数据区域生 ...

随机推荐

  1. mongoose使用简记

    mongodb中集合相当于表,常用指令 mongo 进入数据库 use yourdatabase 来选择你的数据集,这个跟关系型中的一样 show collections 来查看你数据集中的表,col ...

  2. 【leetcode 简单】第十题 实现strStr()

    实现 strStr() 函数. 给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始).如果不存在,则返 ...

  3. cookie知识点概述

    cookie是什么 这个讲起来很简单,了解http的同学,肯定知道,http是一个不保存状态的协议,什么叫不保存状态,就是一个服务器是不清楚是不是同一个浏览器在访问他,在cookie之前,有另外的技术 ...

  4. Go语言 6 结构体、方法和接口

    文章由作者马志国在博客园的原创,若转载请于明显处标记出处:http://www.cnblogs.com/mazg/ Go学习群:415660935 结构体(struct)是由一系列具有相同类型或不同类 ...

  5. Java从零到企业级电商项目实战

    欢迎关注我的微信公众号:"Java面试通关手册"(坚持原创,分享各种Java学习资源,面试题,优质文章,以及企业级Java实战项目回复关键字免费领取)回复关键字:"电商项 ...

  6. ORACLE ASM中查询表空间使用情况、数据文件路径、裸设备磁盘总大小剩余大小

    在ASM中:查询所有磁盘名称.总大小.剩余大小:单位MB-----查看组的信息(总大小)select name,total_mb, free_mb from v$asm_diskgroup; ---查 ...

  7. JVM的分区+查看GC对象是否存活+3种GC算法+7种垃圾收集器+如何减少GC次数

    一.JVM的分区:   1.程序计数器(私有) 程序计数器是一块较小的内存分区,你可以把它看做当前线程所执行的字节码的指示器. 在虚拟机的概念模型里,字节码解释器工作时,就是通过改变计数器的值来选择下 ...

  8. Firefox缓存文件夹位置设置及清除缓存方法

    地址栏敲入: about:config, 新建一个"browser.cache.disk.parent_directory", 并设置为你要的缓存文件夹, 例如:  "F ...

  9. hashCode()与equals()区别

    这两个方法均是超类Object自带的成员方法.Object类是所有Java类的祖先.每个类都使用 Object 作为超类.所有对象(包括数组)都实现这个类的方法.在不明确给出超类的情况下,Java会自 ...

  10. udev和rules使用规则

    本文以通俗的方法阐述 udev 及相关术语的概念.udev 的配置文件和规则文件,然后以 Red Hat Enterprise Server 为平台演示一些管理设备文件和查询设备信息的实例.本文会使那 ...