h-a-p-p-e-n-before原则的理解

2023-10-31

 happen-before 可真是一个经典又老生常谈的话题,规则一共就八条,但看起来总有种抓不住重点的感觉。今天再整理一下对这八条规则的理解。

  首先我的理解是 happen-before 的语义与在什么什么之前发生完全没有关系,其语义是如果 A hapen-bfore B,那么 A 的结果对 B 是可见的。通过这些规则可以保证程序按我们预想的方式运转。

         我个人理解中将该原则分两部分理解,单线程与多线程环境下的HB。单线程下通过语义分析数据依赖关系,编译器和处理器可以合理的优化我们的代码。但是多线程情况下不同线程间的数据依赖关系有我们定义,处理器与编译器都无法通过分析感知,HB 原则定义了某些特定场景下多线程间的数据依赖关系。

  总的来说,HB 原则是对单线程环境下的指令重排序以及多线程环境下的线程间数据的一致性进行的约束。单线程情况下保证串行语义,多线程情况下因为数据的一致性需要我们自己声明和保证,所以 JVM 自行保证了 HB 原则中提出的它认为必须要保证一致性的情况。

  1. 单线程happen-before原则:在同一个线程中,书写在前面的操作happen-before后面的操作。

  首先是单线程的 HB ,前面的操作产生的结果必须对后面的操作可见。而不是前面的操作必须先于后面的操作执行,比如按照 as-if-serial 语义,没有数据依赖的两条指令是可以进行重排序的。而这种情况对于 HB 原则来说,因为两条指令都没有产生对方需要的结果,而不需要对对方可见,及时执行顺序被调转也是符合 HB 原则的。

  2. 锁的happen-before原则:同一个锁的unlock操作happen-before此锁的lock操作。

  个人理解强调的是解锁操作在多线程环境的可见性。一个线程进行了解锁操作,对于晚于该操作的加锁操作必须能够及时感应到锁的状态变化。解锁操作的结果对后面的加锁操作一定是可见的,无论两个是否在一个线程。

  3. volatile的happen-before原则:  对一个volatile变量的写操作happen-before对此变量的任意操作。

  对 volatile 变量的写操作的结果对于发生于其后的任何操作的结果都是可见的。x86 架构下volatile 通过内存屏障和缓存一致性协议实现了变量在多核心之间的一致性。

  4. happen-before的传递性原则:   如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。

  HB 可以说是两项操作之间的偏序关系,满足偏序关系的各项性质,我们都知道偏序关系中有一条很重要的性质:传递性,所以Happens-Before也满足传递性。这个性质非常重要,通过这个性质可以推导出两个没有直接联系的操作之间存在Happens-Before关系

  5. 线程启动的happen-before原则:同一个线程的start方法happen-before此线程的其它方法。

  start 放法与其它方法可能并没有数据依赖关系,但是显而易见的,为了程序的正确性,我们必须做到这一点。start 方法造成的函数副作用必须对其它方法可见。

  6. 线程中断的happen-before原则:对线程interrupt方法的调用happen-before被中断线程的检测到中断发送的代码。

  interrupt 方法改变的状态必须对后续执行的检测方法可见。

  7. 线程终结的happen-before原则:线程中的所有操作都happen-before线程的终止检测。

  为了安全的关闭线程,线程中的方法造成的函数副作用必须对线程关闭方法可见。

  8. 对象创建的happen-before原则:一个对象的初始化完成先于他的finalize方法调用。

  单线程下对象的创建于销毁存在数据依赖,该条原则强调的是多线程情况下对象初始化的结果必须对发生于其后的对象销毁方法可见。

当你看清人们的真相,于是你知道了,你可以忍受孤独

happen-before是JMM最核心的概念,所以在了解happen-before原则之前,首先需要了解java的内存模型。

JMM内存模型
     java内存模型是共享内存的并发模型,线程之间主要通过读-写共享变量来完成隐式通信。java中的共享变量是存储在内存中的,多个线程由其工作内存,其工作方式是将共享内存中的变量拿出来放在工作内存,操作完成后,再将最新的变量放回共享变量,这时其他的线程就可以获取到最新的共享变量。


     从横向去看看,线程A和线程B就好像通过共享变量在进行隐式通信。这其中有很有意思的问题,如果线程A更新后数据并没有及时写回到主存,而此时线程B读到的是过期的数据,这就出现了 “脏读” 现象。
     为避免脏读,可以通过同步机制(控制不同线程间操作发生的相对顺序)来解决或者通过volatile关键字使得每次volatile变量都能够强制刷新到主存,从而对每个线程都是可见的。

重排序
在执行程序时,为了提高性能,编译器和处理器常常会对指令进行重排序。一般重排序可以分为如下三种:如图,1属于编译器重排序,而2和3统称为处理器重排序。
这些重排序会导致线程安全的问题,一个很经典的例子就是DCL问题。JMM的编译器重排序规则会禁止一些特定类型的编译器重排序;针对处理器重排序,编译器在生成指令序列的时候会通过插入内存屏障指令来禁止某些特殊的处理器重排序。

(1)编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序;
(2)指令级并行的重排序。现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序;
(3)内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行的。

举个例子:

由于A,B之间没有任何关系,对最终结果也不会存在关系,它们之间执行顺序可以重排序。因此可以执行顺序可以是A->B->C或者B->A->C执行最终结果都是3.14,即A和B之间没有数据依赖性。

什么是happen-before
      JMM可以通过happens-before关系向程序员提供跨线程的内存可见性保证(如果A线程的写操作a与B线程的读操作b之间存在happens-before关系,尽管a操作和b操作在不同的线程中执行,但JMM向程序员保证a操作将对b操作可见)。

具体的定义为:
1)如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。

2)两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须要按照happens-before关系指定的顺序来执行。如果重排序之后的执行结果,与按happens-before关系来执行的结果一致,那么这种重排序并不非法(也就是说,JMM允许这种重排序)。

具体的规则:
(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()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。
(6)Join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。
(7)程序中断规则:对线程interrupted()方法的调用先行于被中断线程的代码检测到中断时间的发生。
(8)对象finalize规则:一个对象的初始化完成(构造函数执行结束)先行于发生它的finalize()方法的开始。

利用程序顺序规则(规则1)存在三个happens-before关系:

A happens-before B;
B happens-before C;
A happens-before C。
这里的第三个关系是利用传递性进行推论的。这里的第三个关系是利用传递性进行推论的。

A happens-before B,定义1要求A执行结果对B可见,并且A操作的执行顺序在B操作之前,但与此同时利用定义中的第二条,A,B操作彼此不存在数据依赖性,两个操作的执行顺序对最终结果都不会产生影响,在不改变最终结果的前提下,允许A,B两个操作重排序,即happens-before关系并不代表了最终的执行顺序。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

h-a-p-p-e-n-before原则的理解 的相关文章

  • 我需要在 Java 9 中使用哪个模块才能使用 JPA?

    我正在使用一个需要 JPA 的项目测试 Java 9 javax persistence 类 当我添加module info java并声明我的模块 下的所有类javax persistece包变得不可用 我搜索了很多 但找不到在 Java
  • Spring MVC - 自动查找验证器

    假设我有一个像这样的示例实体类 public class Address 和相应的验证器 Component public AddressValidator implements Validator Override public bool
  • selenium 2.0 中的 isElementPresent

    大家好 我正在使用 webdriver 所以如果我想使用 selenium s rc 函数 isElementPresent 我必须模拟 selenium rc 所以我会执行以下操作 import org openqa selenium B
  • Java中如何对对象数组进行排序?

    我的数组不包含任何字符串 但它包含对象引用 每个对象引用都通过 toString 方法返回名称 id 作者和发布者 public String toString return name n id n author n publisher n
  • Mediaplayer 播放几次后停止播放

    我有一个按钮 按下它会播放一个随机声音剪辑 然后播放另一个声音剪辑 然后通过一个媒体播放器播放另一个声音剪辑 但是多次按下该按钮 15 20 次 后 所有音频都会停止 我在播放最后一个音频剪辑后释放媒体播放器 所以我不认为这是原因 有什么指
  • java中%%是什么意思?

    我是一名 PHP 程序员 想知道这行代码的含义 System out printf exp 3f is 3f n x Math exp x 3f 3f n 和逗号 x 是什么意思 它与C类似printf http java sun com
  • 使用 POJO 仅更新 JOOQ 记录中已更改的字段

    我想使用 POJO 作为源来更新 JOOQ 记录中已更改的字段 Record from Object http www jooq org javadoc 3 8 x org jooq Record html from java lang O
  • python 中的子进程调用以使用 JAVA_OPTS 调用 java jar 文件

    示例代码 import subprocess subprocess call java jar temp jar 如何在上面的命令中指定JAVA OPTS 当我使用上述命令时 我收到 java lang OutOfMemoryError 无
  • Java:如果数组大小未知,如何初始化?

    我要求用户输入 1 到 100 之间的一些数字并将它们分配到一个数组中 数组大小未初始化 因为它取决于用户输入数字的次数 我应该如何分配数组长度 如果用户输入 5 6 7 8 9 5 个数字 则 int list becomes int l
  • SwingUtilities.invokeLater

    我的问题与SwingUtilities invokeLater 我应该什么时候使用它 每次需要更新 GUI 组件时都必须使用吗 它到底有什么作用 是否有替代方案 因为它听起来不直观并且添加了看似不必要的代码 Do I have to use
  • 覆盖Java中的属性[重复]

    这个问题在这里已经有答案了 在 Java 中 我最近有几个项目 我使用了这样的设计模式 public abstract class A public abstract int getProperty public class B exten
  • 如何根据从 jtextfield 和组合框接收的值将数据行添加到 Jtable

    我有一个JFrame表格有JTextFields JCombobox等等 我能够将这些值接收到变量 现在我想将接收到的数据添加到JTable当用户单击 添加 或类似的操作时在新行中 我创造了JTable使用 net beans 的问题是将这
  • 有没有办法删除 JShell 中的导入?

    我正在发现 JShell 并且发现默认添加的导入 jshell gt imports import java io import java math import java net import java nio file import j
  • 莫基托。验证方法参数是特定类

    我有一个方法 void putObject
  • 运行 Espresso 测试时在 Android studio 中找不到属性 android:forceQueryable

    我已经使用 android studio 录制了我的 Android 应用程序 Espresso 测试记录浓缩咖啡测试选项中Run菜单 在记录的最后 我用自己的文件名保存了测试 单击保存按钮后 IDE 会自动在以下位置创建文件Android
  • AWS SQS Batch SendMessageBatchRequest 非常慢

    我的应用程序使用 SendMessageBatchRequest 将每个请求发布 10 条消息到 AWS SQS 每条消息的大小小于250字节 该应用程序预计每天发布约一百万条记录 但要实现这一目标 消息发布的速度非常慢 AmazonSQS
  • Java 执行器和长寿命线程

    我继承了一些使用 Executors newFixedThreadPool 4 的代码运行 4 个长寿命线程来完成应用程序的所有工作 这是推荐的吗 我读过Java 并发实践 https rads stackoverflow com amzn
  • 在edittext android中插入imageview

    我想将 imageview 放在 edittext 中 可能吗 我检查了 evernote 应用程序 它能够将照片放在编辑文本部分 我想让我的应用程序完全相同 我如何才能将从图库中选择的图像视图放入编辑文本中 我首先尝试将 imagevie
  • 在 Spark MLlib 上使用 Java 中的 Breeze

    在尝试从Java使用MLlib时 使用微风矩阵运算的正确方法是什么 例如scala 中的乘法很简单 matrix vector 相应的功能在Java中是如何表达的 有一些方法 例如 colon times 可以通过正确的方式调用 breez
  • 为什么我们不能在函数式接口中重载抽象方法? (爪哇)

    所以我熟悉java中的函数式接口 以及它们与lambda表达式的使用 一个函数式接口只能包含一个抽象方法 当从 lambda 表达式使用这一孤独方法时 您不需要指定其名称 因为接口中只有一个抽象方法 编译器知道这就是您正在引用的方法 Exa

随机推荐

  • 射频电路学习之滤波电路

    文章目录 前言 一 滤波电路的分类 二 滤波电路的主要参数 1 插入损耗 IL 2 波纹系数 3 频带宽度 4 矩形系数 5 阻带抑制 三 滤波电路设计 1 集总参数滤波电路 巴特沃斯滤波电路 切比雪夫滤波电路 归一化滤波电路的变换 电路变
  • Topaz Video AI for mac(视频增强和修复工具)

    Topaz Video AI for Mac是一款视频增强和修复工具 采用了人工智能技术 可以提高视频的清晰度 降噪 去抖动和插帧等 这款软件支持多种视频格式 包括MP4 MOV AVI等 使用Topaz Video AI for Mac
  • Python基础—面向对象(超详版)

    Python基础 面向对象 面向对象简介 什么是面向对象 类与对象 父类与子类 面向对象的特性 单继承与多继承 单继承 多继承 多层继承 封装 多态 重写与调用 python重写 python调用 super函数 前言 个人主页 以山河作礼
  • VS 使用System.Console打印时输出窗口不显示

    在项目属性中勾选 启用visual studio承载进程 可以在输出窗口中的调试打印信息中显示System Console打印信息
  • 安装PYG

    目录 1 通过Anaconda安装 2 通过pip安装 3 尝试历程 参考 1 通过Anaconda安装 conda install pyg c pyg c conda forge 2 通过pip安装 首先通过如下命令获取系统torch和C
  • Python并行处理数据多进程/多线程,榨干你的CPU

    前言 最近在公司实习 给整了个活 像是数学建模一样的数据分析的活 目标是在几个互相有关联的大表中找出满足某条件的那些业务 其中第一步就是把两个表拼起来 就叫它们A和B吧 省略拼表过程中需要的逻辑判断 串行 两个长为M和N的表 在判断中需要一
  • Oracle删除重复数据只保留一条

    问题描述 merge数据时 找不到唯一的源数据 需要先删除有重复的数据 只保留一条 解决方式 查找表中多余的重复记录 多个字段 select from tmp a where a 料号 a 分类码 in select 料号 分类码 from
  • FFmpeg安装与使用+Java API/SpringBoot项目使用

    目录 下载安装 介绍与简单使用 在项目中的使用 配置文件 工具类 使用 下载安装 Download FFmpeg 解压到指定文件夹 介绍与简单使用 FFmpeg是一个开源的跨平台视频和音频处理工具 可以处理各种格式的音视频文件 FFmpeg
  • echarts实现人物树形关系图实例

    写这个栗子的时候主要因为是刚刚做一个项目 要用到人物关系图 所以一直在找合适插件 最后选择echarts2 0的tree图最合适这个关系图了 哈哈 话不多少 现在就把效果图献上吧 ps 照片是网上下载的 莫要见外啊 这个使用的是echart
  • 引入并使用Element

    引入并使用Element 开发工具与关键技术 MVC 作者 汤池 撰写时间 2022 4 7 1 首先需要打开DW软件 新建一个html网页 然后保存到一个文件夹当中 2 然后打开element官网 下载使用Element必须引用的脚本文件
  • 2022/1/22记录网页

    65条消息 102 bert词向量进行文本分类和命名实体识别 u012416045的博客 CSDN博客 bert文本识别https blog csdn net u012416045 article details 88373965 Bert
  • 记一次kafka无法生产发送消息排查经历

    参考 欢迎点击原文 https stackoverflow com questions 37902167 kafka error while fetching metadata with correlation id leader not
  • c语言中简单变量必须,C语言规定,简单变量做实参时,它和对应形参之间的数据传递方式是()。 (A)地址传递 (B)单向值传递 (C)由...

    参考答案如下 语言规应形C由一般来说 各种调查报告在结构上都可分成哪几个部分 定地址B单演示PPT的结尾部分一般建议展示以下内容 通过startService再次启动Service时 简单间不会再执行onCreate 函数 变量蛋白质分子的
  • 图机器学习课程笔记7

    维生素C吃多了会上火 个人CSDN博文目录 cs224w 图机器学习 2021冬季课程学习笔记集合 目录 思维大纲 中文笔记 思维大纲 中文笔记
  • 树莓派入门教程 - 0 - 准备篇 - 0.3 树莓派串口访问终端

    电脑需要提前安装的工具 SSH 串口 终端神器 MobaXterm 官网下载链接 https mobaxterm mobatek net download html 使用到的硬件 USB转TTL模块 CH340 和 CP2102均可 CP2
  • 1.2-知识图谱有什么用?

    知识图谱经过几年的发展已经得到广泛的应用 当知识图谱遇上人工智能 更加突显出了它的优势和价值 最先应用于搜索 最典型的就是在谷歌搜索引擎里面应用 谷歌是在2012年率先提出来知识图谱的概念 提出这个概念的最主要的目的就是用于改善它的搜索引擎
  • ospf小结

    ospf卡住的原因 route id相同 认证不通过 区域ID不同 特殊区域标示不一致 优先级都为0 无法选举 MTU值不匹配 包的交互不完整 网络中丢包等 LSA加载不完全 hello包时间不一致 MA网络掩码不一致 虚链路建邻ABR的两
  • JVM常量池最全详解-常量池/运行时常量池/字符串常量池/基本类型常量池,看这一篇就够了

    JVM常量池最全详解 常量池 运行时常量池 字符串常量池 基本类型常量池 看这一篇就够了 常量池详解 1 字面量和符号引用 1 1 字面量 1 2 符号引用 2 常量池vs运行时常量池 3 常量池 静态常量池 4 运行时常量池 5 字符串常
  • Introduction to Scientific Programing and Simulation Using R chapter 04 答案

    strong span style font size 32px Ex1 span strong span style color 33cc00 programe cha4 6 ex1 img src https img blog csdn
  • h-a-p-p-e-n-before原则的理解

    happen before 可真是一个经典又老生常谈的话题 规则一共就八条 但看起来总有种抓不住重点的感觉 今天再整理一下对这八条规则的理解 首先我的理解是 happen before 的语义与在什么什么之前发生完全没有关系 其语义是如果