Java内存模型之happens-before概念详解


Posted in Java/Android onJune 13, 2021

简介

happens-before是JMM的核心概念。理解happens-before是了解JMM的关键。

1、设计意图

JMM的设计需要考虑两个方面,分别是程序员角度和编译器、处理器角度:

  • 程序员角度,希望内存模型易于理解、易于编程。希望是一个强内存模型。
  • 编译器和处理器角度,希望减少对它们的束缚,以至于编译器和处理器可以做更多的性能优化。希望是一个弱内存模型。

​因此JSR-133专家组设计JMM的核心目标就两个:

为程序员提供足够强的内存模型对编译器和处理器的限制尽可能少

​下面通过一段代码来看JSR-133如何实现这两个目标:

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

上述代码存在如下happens-before关系:

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

这3个happens-before关系中,第二个和第三个是必须的,而第一个是非必须的(A、B操作之间重排序,程序执行结果不会发生改变)。
JMM把happens-before要求禁止的重排序分为下面的两类:

  • 会改变程序执行结果的重排序
  • 不会改变程序执行结果的重排序

JMM对这两种不同性质的重排序,采取了不同的策略:

  • 对于会改变程序执行结果的重排序,JMM要求编译器和处理器必须禁止
  • 对于不会改变程序执行结果的重排序,JMM不做要求(JMM运行)

JMM设计示意图:

Java内存模型之happens-before概念详解

JMM设计示意图

总结:

  • JMM给程序员提供的happens-before规则能满足程序员的需求。简单易懂,具有足够强的内存可见性保证。
  • JMM对编译器和处理器的束缚尽可能少。遵循的原则是:不改变程序的执行结果(正确同步或单线程执行),编译器和处理器可以任意优化。

 2、happens-before的定义

起源:
happens-before规则来源于Leslie Lamport《Time, Clocks and the Ordering of Events in a Distributed System》。该论文中使用happens-before来定义分布式系统中事件之间的偏序关系(partial ordering),该文中给出了一个分布式算法,能用来将偏序关系扩展为某种全序关系。

Java中的应用:
JSR-133使用happens-before来指定两个操作之间的执行顺序。JMM可以通过happens-before关系向程序员提供跨线程的内存可见性保证。

《JSR-133:Java Memory Model and Thread Specification》对happens-before关系的定义如下:

如果操作A happens-before 操作B,那么A操作的执行结果将会对操作B可见,且操作A的执行顺序排在操作B之前——JMM对程序员的承诺两个操作存在happens-before关系,并不意味着Java平台的具体实现必须按照happens-before的顺序来执行。如果重排序不改变程序执行结果(与happens-before)规则一致,那么这种重排序是不非法的(JMM允许这种重排序)。——JMM对编译器和处理器的束缚原则

happens-before和as-if-serial语义:
从上述来看,happens-before和as-if-serial语义本质上是一回事

  • as-if-serial语义保证单线程内程序的执行结果不被改变,happens-before关系保证正确同步的多线程程序的执行结果不改变
  • as-if-serial语义给编程者一种单线程是按程序顺序执行的幻境;happens-before关系给编程者一种正确同步的多线程是按照happens-before指定的顺序执行的幻境。

两者的目的都是为了在不改变程序执行结果的前提下,尽可能的提高程序的执行效率。

3、happens-before规则

《JSR-133:Java Memory Model and Thread Specification》定义了如下happens-before规则

  • 程序顺序规则
  • 监视器锁规则
  • volatile变量规则
  • 传递性
  • start()规则
  • join()规则

3.1 volatile写-读

volatile写-读建立的happens-before关系

Java内存模型之happens-before概念详解

happens-before关系示意图

分析上图:

  1. 1 happens-before 2和3 happens-before 4由程序顺序规则产生。由于编译器和处理器遵循as-if-serial语义,也就是说,as-if-serial语义保证了程序顺序规则。因此可以把程序顺序规则看成是对as-if-serial语义的“封装”。
  2. 2 happens-before 3 是有volatile规则产生。一个volatile变量的读,总是能看到(任意线程)对这个volatile变量的最后写入。
  3. 1 happens-before 4 是由传递性规则产生的。这里的传递性是由volatile的内存屏障插入策略和volatile的编译器重排序规则来共同保证的。

3.2 start()规则

假设线程A在执行的过程中,通过执行ThreadB.start()来启动线程B;同时,假设线程A在执行ThreadB.start()之前修改了一个共享变量,线程B在执行后会读取这些共享变量。
start()程序对应的happens-before关系图:

Java内存模型之happens-before概念详解

分析上图:

  • 1 happens-before
  • 2 由程序顺序规则产生2 happens-before 4 由start规则产生
  • 1 happens-before 4 由传递性规则产生

因此线程A执行ThreadB.start()之前对共享变量所做的修改,在线程B执行后都将确保对线程B可见。

3.3 join()规则

假设线程A执行的过程中,通过执行ThreadB.join()来等待线程B终止;则线程B在终止之前修改了一些共享变量,线程A从ThreadB.join()返回后会读这些共享变量。
join()程序的happens-before关系图:

Java内存模型之happens-before概念详解

分析上图:

  1. 2 happens-before
  2. 4 由join()规则产生4 happens-before 5 由程序顺序规则产生
  3. 2 happens-before 5 由传递性规则产生

因此线程A执行操作ThreadB.join()并成功返回,线程B中任意操作都将对线程A可见。

文章总结至《Java并发编程艺术》,下篇总结“双重检查所定与延迟初始化”,敬请关注。

以上就是Java内存模型之happens-before概念详解的详细内容,更多关于Java内存模型 happens-before的资料请关注三水点靠木其它相关文章!

Java/Android 相关文章推荐
Spring Bean的实例化之属性注入源码剖析过程
Jun 13 Java/Android
解决SpringCloud Feign传对象参数调用失败的问题
Jun 23 Java/Android
详解Java实现数据结构之并查集
Jun 23 Java/Android
Spring Boot两种全局配置和两种注解的操作方法
Jun 29 Java/Android
Java数组与堆栈相关知识总结
Jun 29 Java/Android
Spring实现内置监听器
Jul 09 Java/Android
Java数据开发辅助工具Docker与普通程序使用方法
Sep 15 Java/Android
java多态注意项小结
Oct 16 Java/Android
Android自定义ScrollView实现阻尼回弹
Apr 01 Java/Android
解决springboot druid数据库连接失败后一直重连的方法
Apr 19 Java/Android
Java Spring Lifecycle的使用
May 06 Java/Android
Java代码规范与质量检测插件SonarLint的使用
Aug 05 Java/Android
Java实战之用Swing实现通讯录管理系统
在Java中Collection的一些常用方法总结
Win10系统下配置Java环境变量
SpringCloud Alibaba 基本开发框架搭建过程
Spring Bean的实例化之属性注入源码剖析过程
Java输出Hello World完美过程解析
Java方法重载和方法重写的区别到底在哪?
Jun 11 #Java/Android
You might like
在php中判断一个请求是ajax请求还是普通请求的方法
2011/06/28 PHP
PHP页面中文乱码分析
2013/10/29 PHP
微信公众号判断用户是否已关注php代码解析
2016/06/24 PHP
php 使用html5 XHR2实现上传文件与进度显示功能示例
2020/03/03 PHP
让iframe框架网页在任何浏览器下自动伸缩
2006/08/18 Javascript
Jquery attr("checked") 返回checked或undefined 获取选中失效
2013/10/10 Javascript
javascript+HTML5的Canvas实现Lab单车动画效果
2015/08/07 Javascript
Javascript获取数组中的最大值和最小值的方法汇总
2016/01/01 Javascript
JS实现保留n位小数的四舍五入问题示例
2016/08/03 Javascript
vue.js将unix时间戳转换为自定义时间格式
2017/01/03 Javascript
JS模拟超市简易收银台小程序代码解析
2017/08/18 Javascript
深入理解Vue 组件之间传值
2018/08/16 Javascript
vue-cli V3.0版本的使用详解
2018/10/24 Javascript
JavaScript实现的鼠标跟随特效示例【2则实例】
2018/12/22 Javascript
其实你可以少写点if else与switch(推荐)
2019/01/10 Javascript
js实现盒子移动动画效果
2020/08/09 Javascript
[05:17]DOTA2誓师:今天我们在这里 明天TI4等我!
2014/03/26 DOTA
[01:45]典藏宝瓶2+祈求者身心——这就是DOTA2TI9总奖金突破3000万美元的秘密
2019/07/21 DOTA
[01:38]女王驾到——至宝魔廷新尊技能&特效展示
2020/06/16 DOTA
Python numpy 点数组去重的实例
2018/04/18 Python
python 把列表转化为字符串的方法
2018/10/23 Python
Python多线程实现支付模拟请求过程解析
2020/04/21 Python
PyQt5实现仿QQ贴边隐藏功能的实例代码
2020/05/24 Python
详解Python中list[::-1]的几种用法
2020/11/16 Python
Python如何telnet到网络设备
2021/02/18 Python
CSS3的calc()做响应模式布局的实现方法
2017/09/06 HTML / CSS
实习自我鉴定模板
2013/09/28 职场文书
服装创业计划书范文
2014/02/05 职场文书
监督检查工作方案
2014/05/28 职场文书
公司经理任命书
2014/06/05 职场文书
奶茶店创业计划书
2014/08/14 职场文书
农业局党的群众路线教育实践活动整改方案
2014/09/20 职场文书
教学改革问题查摆整改措施
2014/09/27 职场文书
2015年服务员个人工作总结
2015/05/27 职场文书
感恩主题班会教案
2015/08/12 职场文书
企业内部管理控制:采购授权审批制度范本
2020/01/19 职场文书