为什么会有内存屏障

  • 每个CPU都会有自己的缓存(有的甚至L1,L2,L3),缓存的目的就是为了提高性能,避免每次都要向内存取。但是这样的弊端也很明显:不能实时的和内存发生信息交换,分在不同CPU执行的不同线程对同一个变量的缓存值不同。
  • 用volatile关键字修饰变量可以解决上述问题,那么volatile是如何做到这一点的呢?那就是内存屏障,内存屏障是硬件层的概念,不同的硬件平台实现内存屏障的手段并不是一样,java通过屏蔽这些差异,统一由jvm来生成内存屏障的指令。

内存屏障是什么

  • 硬件层的内存屏障分为两种:Load BarrierStore Barrier即读屏障和写屏障。
  • 内存屏障有两个作用:
  1. 阻止屏障两侧的指令重排序;
  2. 强制把写缓冲区/高速缓存中的脏数据等写回主内存,让缓存中相应的数据失效。
  • 对于Load Barrier来说,在指令前插入Load Barrier,可以让高速缓存中的数据失效,强制从新从主内存加载数据;
  • 对于Store Barrier来说,在指令后插入Store Barrier,能让写入缓存中的最新数据更新写入主内存,让其他线程可见。

java内存屏障

  • java的内存屏障通常所谓的四种即LoadLoad,StoreStore,LoadStore,StoreLoad实际上也是上述两种的组合,完成一系列的屏障和数据同步功能。
  • LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
  • StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
  • LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
  • StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能

volatile语义中的内存屏障

  • volatile的内存屏障策略非常严格保守,非常悲观且毫无安全感的心态:

在每个volatile写操作前插入StoreStore屏障,在写操作后插入StoreLoad屏障;

在每个volatile读操作前插入LoadLoad屏障,在读操作后插入LoadStore屏障;

  • 由于内存屏障的作用,避免了volatile变量和其它指令重排序、线程之间实现了通信,使得volatile表现出了锁的特性。

final语义中的内存屏障

  • 对于final域,编译器和CPU会遵循两个排序规则:
  1. 新建对象过程中,构造体中对final域的初始化写入和这个对象赋值给其他引用变量,这两个操作不能重排序;
  2. 初次读包含final域的对象引用和读取这个final域,这两个操作不能重排序;(意思就是先赋值引用,再调用final值)
  • 总之上面规则的意思可以这样理解,必需保证一个对象的所有final域被写入完毕后才能引用和读取。这也是内存屏障的起的作用:
  • 写final域:在编译器写final域完毕,构造体结束之前,会插入一个StoreStore屏障,保证前面的对final写入对其他线程/CPU可见,并阻止重排序。
  • 读final域:在上述规则2中,两步操作不能重排序的机理就是在读final域前插入了LoadLoad屏障。
  • X86处理器中,由于CPU不会对写-写操作进行重排序,所以StoreStore屏障会被省略;而X86也不会对逻辑上有先后依赖关系的操作进行重排序,所以LoadLoad也会变省略。

转自: https://www.jianshu.com/p/2ab5e3d7e510


JMM模型

JMM 全称是 Java Memory Model. 什么是 JMM 呢? 通过前面的分析发现,导致可见性问题的根本原因是缓存 以及重排序。 而 JMM 实际上就是提供了合理的禁用缓存 以及禁止重排序的方法。所以它最核心的价值在于解决可 见性和有序性。
 
它解决了 CPU 多级缓存、处理器优化、指令重排序 导致的内存访问问题,保证了并发场景下的可见性。

需要注意

JMM 并没有限制执行引擎使用处理器的寄 存器或者高速缓存来提升指令执行速度,也没有限制编译 器对指令进行重排序,也就是说在 JMM 中,也会存在缓存 一致性问题和指令重排序问题。只是 JMM 把底层的问题抽 象到 JVM 层面,再基于 CPU 层面提供的内存屏障指令, 以及限制编译器的重排序来解决并发问题

 
JMM 抽象模型分为主内存、工作内存;主内存是所有线程 共享的,一般是实例对象、静态字段、数组对象等存储在 堆内存中的变量。工作内存是每个线程独占的,线程对变 量的所有操作都必须在工作内存中进行,不能直接读写主 内存中的变量,线程之间的共享变量值的传递都是基于主 内存来完成
 
Java 内存模型底层实现可以简单的认为:通过内存屏障 (memory barrier)禁止重排序,即时编译器根据具体的底层 体系架构,将这些内存屏障替换成具体的 CPU 指令。对 于编译器而言,内存屏障将限制它所能做的重排序优化。 而对于处理器而言,内存屏障将会导致缓存的刷新操作。 比如,对于 volatile,编译器将在 volatile 字段的读写操作 前后各插入一些内存.
 

JMM 是如何解决可见性有序性问题的

 简单来说,JMM 提供了一些禁用缓存以及进制重排序的方 法,来解决可见性和有序性问题。这些方法大家都很熟悉: volatile、synchronized、final.等.
 
从源代码到最终执行的指令,可能会经过三种重排序。

  • 编译器的重排序,JMM 提供了禁止特定类型的编译器重排 序。
  • 处理器重排序,JMM 会要求编译器生成指令时,会插入内 存屏障来禁止处理器重排序

java内存屏障的更多相关文章

  1. Java 内存屏障

    内存屏障(Memory Barrier,或有时叫做内存栅栏,Memory Fence)是一种CPU指令,用于控制特定条件下的重排序和内存可见性问题.Java编译器也会根据内存屏障的规则禁止重排序. 内 ...

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

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

  3. Java内存模型原理,你真的理解吗?

    [51CTO.com原创稿件]这篇文章主要介绍模型产生的问题背景,解决的问题,处理思路,相关实现规则,环环相扣,希望读者看完这篇文章后能对 Java 内存模型体系产生一个相对清晰的理解,知其然知其所以 ...

  4. volatile 和 内存屏障

    接下来看看volatile是如何解决上面两个问题的: 被volatile修饰的变量在编译成字节码文件时会多个lock指令,该指令在执行过程中会生成相应的内存屏障,以此来解决可见性跟重排序的问题. 内存 ...

  5. jvm(三)指令重排 & 内存屏障 & 可见性 & volatile & happen before

    参考文档: https://tech.meituan.com/java-memory-reordering.html http://0xffffff.org/2017/02/21/40-atomic- ...

  6. Java原理领悟-JMM(java内存模型认知)

    总线锁.缓存锁.MESI缓存一致性协议.CPU 层面的内存屏障 1.JMM定义: Java Memory Model(java内存模型)是一系列的Java虚拟机平台对开发者提供的多线程环境下的内存可见 ...

  7. Java多线程学习笔记之三内存屏障与Java内存模型

    基本内存屏障 处理器支持那种内存重排序,就会提供能够禁止相应内存重排序的的指令,这些指令就被成为基本内存屏障:StroeLoad屏障.StroeLoad屏障.LoadLoad屏障.LoadStore屏 ...

  8. Java内存模型(MESI、内存屏障、volatile和锁及final内存语义)

    JMM (Java内存模型) Java线程的实现 实现线程主要有三种方式,Java线程从JDK1.3后采用第一种方式实现: 使用内核线程实现(1:1实现) 使用用户线程实现(1:N实现) 使用用户线程 ...

  9. 关于Java中的内存屏障

    如何打破双亲委派机制 继承ClassLoader类后重写loadClass方法 如何指定自定义ClassLoader中的parent 默认parent是appClassLoader,可以通过Class ...

随机推荐

  1. Java多线程_生产者消费者模式2

    在我的上一条博客中,已经介绍到了多线程的经典案列——生产者消费者模式,但是在上篇中用的是传统的麻烦的非阻塞队列实现的.在这篇博客中我将介绍另一种方式就是:用阻塞队列完成生产者消费者模式,可以使用多种阻 ...

  2. 关于函数式接口, printable 自定义

    这段代码在jdk1.8可以使用,  由于我是jdk14, 会报错. 这里可以优化, lambda表达式进一步优化写为:  printString(System.Out::println); 注意案例版 ...

  3. HMM隐马尔可夫模型来龙去脉(二)

    目录 前言 预备知识 一.估计问题 1.问题推导 2.前向算法/后向算法 二.序列问题 1.问题推导 2.维特比算法 三.参数估计问题 1.问题推导 2.期望最大化算法(前向后向算法) 总结 前言 H ...

  4. jsp页面关于isELIgnored="false",页面无法解析数据问题

    问题: 首先确定所取的集合里面是否有值,如果没有先检查集合 如果有,就再jsp页面头部添加: isELIgnored="false" 具体如下: <%@ page langu ...

  5. Jigsaw pre-training:摆脱ImageNet,拼图式主干网络预训练方法 | ECCV 2020

    Jigsaw pre-training以拼图的方式从检测数据集中生成用于主干网络预训练的数据集,而不需要额外的预训练数据集,如ImageNet.另外为了让网络更好的适应拼图数据,论文提出ERF-ada ...

  6. org.xml.sax.SAXParseException: The processing instruction target matching "[xX][mM][lL]" is not allowed(转)

    xml文件不能被正确解析/The processing instruction target matching "[xX][mM][lL]" is not allowed. The ...

  7. springboot x.x.x RELEASE不同版本的差异

    springboot 1.x.x RELEASE的 application.properties配置 server.context-path=/ server.port=8080 server.ses ...

  8. Linked List 单向链表

    Linked List 链表的理解 小结 链表是以节点的方式来储存的 每个节点包括 data域:存放数据,next域:指向下一个节点 如图:发现链表的各个节点不一定是连续储存的 链表分为带头节点的链表 ...

  9. xmake v2.3.7 发布, 新增 tinyc 和 emscripten 工具链支持

    xmake 是一个基于 Lua 的轻量级跨平台构建工具,使用 xmake.lua 维护项目构建,相比 makefile/CMakeLists.txt,配置语法更加简洁直观,对新手非常友好,短时间内就能 ...

  10. PHP学习路线图(转)

    PHP学习路线图 在网上很多人公布了太多的PHP学习路线图,本人在互联网公司工作十余年,也带了很多PHP入门的新手,将他们的一些问题和学习路线图为大家整理出来,希望很多小白少走弯路. 一. 网上某些错 ...