有时候编译器、处理器的优化会导致runtime与我们设想的不一样,为此Java对编译器和处理器做了一些限制,JAVA内存模型(JMM)将这些抽象出来,这样编写代码时就无需考虑那么多底层细节,并保证“只要遵循JMM的规则编写程序,其运行结果一定是正确的”。

JMM的抽象结构

 在Java中,所有的实例、静态变量存储在堆内存中,堆内存是可以在线程间共享的,这部分也称为共享变量。而局部变量、方法定义参数、异常处理参数是在栈中的,栈内存不在线程间共享。

 而由于编译器、处理器的优化,会导致共享变量出现可见性问题,像在多核处理器中(multi-processor),线程可以在不同的处理器上执行,而处理器之间缓存不一致,会使共享变量出现可见性问题,有可能两个线程看到同一个变量不同值。

 JMM将这些硬件做的优化抽象成每个线程都有一个本地内存。需要读写共享变量时,从主内存中拷贝一份到本地内存。当写共享变量时,先写到本地内存中去,在将来某个时间再刷新到主内存中。当再次读共享变量时,则只会从本地内存中读取。

 这样线程间通讯就需要经过两步:

  1. 写线程:刷新本地内存到主内存中去
  2. 读线程:从主内存读取更新后的值

 这样在写-读之间就有一个延迟:本地内存什么时候刷新到主内存中去?导致可见性问题,不同线程可能看到的共享变量不一样。

happens-before

 从字面上看happens-before的意思是“发生在此之前”。这是java对程序执行顺序制定的规则,实现同步必须遵循该规则。这样程序员只需要写出正确的同步程序,happens-before保证运行结果不会错。

A happens-before B,不仅仅表示A在B之前执行,还意味着A的执行结果对B可见,这保证了可见性。

A happens-before B,A也不一定要在B之前执行,如果AB交替,执行结果任然正确,则允许编译器、处理器进行优化重排序。所以只要程序结果正确,编译器、处理器怎么优化,怎么重排序都没问题,都是好的。

happens-before规则

  1. 程序顺序规则:在一个线程中,前面的操作happens-before后面的操作
  2. 锁规则:对同一个锁,解锁happens-before加锁
  3. volatile域规则:写volatile变量,happens-before后面任意一个读这个volatile变量的操作
  4. 传递性:A happens-before B,B happens-before C,则A happens-before C
  5. start()规则:如果线程A执行ThreadB.start() 那么ThreadB.start() happens-before 线程B中任何操作
  6. join()规则:如果线程A执行ThreadB.join(),那么线程B中的所有操作happens-before ThreadB.join()

 下面这个示例有助于理解happens-before

double pi = 3.14; //A
double r = 1.0; //B
double area = pi * r *r; //C

 这里有三个happens-before关系,规则1、2是程序顺序规则,规则3是传递性规则推导出来的:

  1. A happens-before B
  2. B happens-before C
  3. A happens-before C

 C依赖于A、B,但是A和B谁也不依赖。所以即使A和B重排序,执行结果也不会发生变化,这种重排序,JMM是运行的。

 下面两种执行顺序的结果都是正确的。

我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan

Java内存模型—JMM的更多相关文章

  1. Java内存模型JMM与可见性

    Java内存模型JMM与可见性 标签(空格分隔): java 1 何为JMM JMM:通俗地讲,就是描述Java中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这 ...

  2. 多线程并发之java内存模型JMM

    多线程概念的引入是人类又一次有效压寨计算机的体现,而且这也是非常有必要的,因为一般运算过程中涉及到数据的读取,例如从磁盘.其他系统.数据库等,CPU的运算速度与数据读取速度有一个严重的不平衡,期间如果 ...

  3. Java内存模型JMM 高并发原子性可见性有序性简介 多线程中篇(十)

    JVM运行时内存结构回顾 在JVM相关的介绍中,有说到JAVA运行时的内存结构,简单回顾下 整体结构如下图所示,大致分为五大块 而对于方法区中的数据,是属于所有线程共享的数据结构 而对于虚拟机栈中数据 ...

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

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

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

    原文地址:全面理解Java内存模型(JMM)及volatile关键字 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型( ...

  6. 什么是Java内存模型(JMM)

    什么是java内存模型 缓存一致性问题 在现代计算机中,因为CPU的运算速度远大于内存的读写速度,因此为了不让CPU在计算的时候因为实时读取内存数据而影响运算速度,CPU会加入一层缓存,在运算之前缓存 ...

  7. 对多线程java内存模型JMM

    多线程概念的引入体现了人类重新有效压力寨计算机.这是非常有必要的,由于所涉及的读数据的过程中的一般操作,如从磁盘.其他系统.数据库等,CPU计算速度和数据读取速度已经严重失衡.假设印刷过程中一个线程将 ...

  8. 深入理解Java内存模型JMM与volatile关键字

    深入理解Java内存模型JMM与volatile关键字 多核并发缓存架构 Java内存模型 Java线程内存模型跟CPU缓存模型类似,是基于CPU缓存模型来建立的,Java线程内存模型是标准化的,屏蔽 ...

  9. Java内存模型(JMM)详解

    在Java JVM系列文章中有朋友问为什么要JVM,Java虚拟机不是已经帮我们处理好了么?同样,学习Java内存模型也有同样的问题,为什么要学习Java内存模型.它们的答案是一致的:能够让我们更好的 ...

  10. Java并发编程:Java内存模型JMM

    简介 Java内存模型英文叫做(Java Memory Model),简称为JMM.Java虚拟机规范试图定义一种Java内存模型来屏蔽掉各种硬件和系统的内存访问差异,实现平台无关性. CPU和缓存一 ...

随机推荐

  1. PHP(一)OOP基础

    [面向过程&面向对象] 1.面向过程:专注于解决一件事情的过程.最大的特点,是有一个个函数来实现功能需求 2.面向对象:专注于有哪一个对象来实现这个功能,最大的特点,时产生一个个具有属性和方法 ...

  2. 【吐槽】关于256个 class可以覆盖一个id的问题

    还是说今天下午面试的事情,被面试官问了 40多分钟的问题,我觉得丫 一定是从哪个网站down了几份面试题,自个儿整合了一下,然后挨个问,刚开始感觉哟,不错哦,面试官懂的蛮多的. 然后问到某个问题之后, ...

  3. [转]彻底弄懂css中单位px和em,rem的区别

    难怪会问我 rem 和 em, 这俩或在移动端还是很有必要学习的. root em OK? 国内的设计师大都喜欢用px,而国外的网站大都喜欢用em和rem,那么三者有什么区别,又各自有什么优劣呢? P ...

  4. Linux 文件系统模型

    声明:本文仅限于 cnblogs 发布,其他第三方网站均为盗版,原文地址:Linux 文件系统模型 在 Linux 环境下有过一些经历的同学可能都会遇到一个问题,这个问题就是往机器上插入 U盘 或者其 ...

  5. Java钉钉开发_03_通讯录管理之 人员管理 和 部门管理

    一.本节要点 1.通讯录权限 ISV(应用服务商)默认无管理通讯录的权限,企业应用默认有所有通讯录权限. 2.数据传输格式—JSON 请参见: Java_数据交换_fastJSON_01_用法入门 二 ...

  6. JS--我发现,原来你是这样的JS:面向对象编程OOP[3]--(JS继承)

    一.面向对象编程(继承) 这篇博客是面向对象编程的第三篇,JS继承.继承顾名思义,就是获取父辈的各种"财产"(属性和方法). 怎么实现继承? 我们的JavaScript比较特别了, ...

  7. 对于php-fpm和cgi,还有并发响应的理解

    参考链接: - https://www.zhihu.com/question/64414628   php fpm 进程数和并发数是什么关系? - https://segmentfault.com/q ...

  8. 设计模式的征途—11.外观(Facade)模式

    在软件开发中,有时候为了完成一项较为复杂的功能,一个类需要和多个其他业务类交互,而这些需要交互的业务类经常会作为一个完整的整体出现,由于涉及的类比较多,导致使用时代码较为复杂,此时,特别需要一个类似服 ...

  9. 关于web变量配置问题

    关于web变量配置问题 webservice里面接口完成时考虑到接口可能用在不同的服务器,不同的数据库所以将链接地址修改成变量,在webconfig里面去修改它更加的容易方便 假始根据在同一个服务器里 ...

  10. 《Linux命令行与shell脚本编程大全》第十三章 更多的结构化命令

    本章讨论bash shell的循环命令for.while和until 13.1 for命令 重复执行一系列命令在编程中很常见. bash shell提供了for命令,允许你创建一个遍历一系列值的循环. ...