Java内存模型之happens-before概念详解
作者:Liziba 发布时间:2023-11-23 03:11:50
简介
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关系:
A happens-before B
B happens-before C
A happens-before C
这3个happens-before关系中,第二个和第三个是必须的,而第一个是非必须的(A、B操作之间重排序,程序执行结果不会发生改变)。
JMM把happens-before要求禁止的重排序分为下面的两类:
会改变程序执行结果的重排序
不会改变程序执行结果的重排序
JMM对这两种不同性质的重排序,采取了不同的策略:
对于会改变程序执行结果的重排序,JMM要求编译器和处理器必须禁止
对于不会改变程序执行结果的重排序,JMM不做要求(JMM运行)
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关系
分析上图:
1 happens-before 2和3 happens-before 4由程序顺序规则产生。由于编译器和处理器遵循as-if-serial语义,也就是说,as-if-serial语义保证了程序顺序规则。因此可以把程序顺序规则看成是对as-if-serial语义的“封装”。
2 happens-before 3 是有volatile规则产生。一个volatile变量的读,总是能看到(任意线程)对这个volatile变量的最后写入。
1 happens-before 4 是由传递性规则产生的。这里的传递性是由volatile的内存屏障插入策略和volatile的编译器重排序规则来共同保证的。
3.2 start()规则
假设线程A在执行的过程中,通过执行ThreadB.start()来启动线程B;同时,假设线程A在执行ThreadB.start()之前修改了一个共享变量,线程B在执行后会读取这些共享变量。
start()程序对应的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关系图:
分析上图:
2 happens-before
4 由join()规则产生4 happens-before 5 由程序顺序规则产生
2 happens-before 5 由传递性规则产生
因此线程A执行操作ThreadB.join()并成功返回,线程B中任意操作都将对线程A可见。
文章总结至《Java并发编程艺术》,下篇总结“双重检查所定与延迟初始化”,敬请关注。
来源:https://blog.csdn.net/qq_41125219/article/details/117855918


猜你喜欢
- Threadlocal有什么用:简单的说就是,一个ThreadLocal在一个线程中是共享的,在不同线程之间又是隔离的(每个线程都只能看到自
- 本文实例为大家分享了java导出百万以上数据的excel文件,供大家参考,具体内容如下1.传统的导出方式会消耗大量的内存,2003每个she
- 在java开发中,类、接口、方法,都需要进行注释,注释内容如图:注释中的基本元素有:描述、作者、创建日期。可增加元素有:修改日期、修改内容、
- android Notification实例详解1.使用Builder模式来创建2.必须要设置一个smallIcon,还可以设置setTic
- 在用C#开发Web应用时有个痛点,就是本机用VS开启Web应用调试时外部机器无法访问此Web应用。这里将会介绍如何通过设置允许局域网和外网机
- JMM与问题引入为啥先说JMM,因为CAS的实现类中维护的变量都被volatile修饰, 这个volatile 是遵循JMM规范(不是百分百
- 很多时候我们开发的软件需要向用户提供软件参数设置功能,例如我们常用的QQ,用户可以设置是否允许陌生人添加自己为好友。对于软件配置参数的保存,
- 在界面设计中,一个容器要放置许多组件,为了美观,为组件安排在容器中的位置,这就是布局设计。java.awt中定义了多种布局类,每种布局类对应
- 本文实例为大家分享了openoffice+jodconverter-code-3.0-bate4实现ppt转图片的具体代码,供大家参考,具体
- 一、简单介绍Unity 游戏实例开发集合,使用简单易懂的方式,讲解常见游戏的开发实现过程,方便后期类似游戏开发的借鉴和复用。本节介绍,Fly
- main方法调用spring的service将业务层类配置到Spring中:<bean id="customerServic
- 基于IDEA生成可执行jar包1.编写class的代码,注意一定要有main()方法才可以生成jar包,main()方法可以没有内容。例如:
- 本文实例为大家分享了Android实现歌词滚动效果的具体代码,供大家参考,具体内容如下自定义TextViewpublic class Ver
- 本文实例为大家分享了java实现推箱子小游戏的具体代码,供大家参考,具体内容如下二维数组二维数组:类似于二维表格(有很多层,每一层有多个房间
- 技术场景在日常的开发、测试或运维的过程中,经常存在这样的场景,开发人员在代码中使用日志工具(log4j、slf4j)记录日志,比如请求ID、
- 今天看一个教程,看到一个颜色渐变的ProgressBar,觉得有点意思,所以记录一番。下面这个是效果图颜色渐变的ProgressBar看到效
- LinkedList与ArrayList都是List接口的具体实现类。LinkedList与ArrayList在功能上也是大体一致,但是因为
- 本文实例讲述了Java实现SSL双向认证的方法。分享给大家供大家参考,具体如下:我们常见的SSL验证较多的只是验证我们的服务器是否是真实正确
- 前言异常崩溃,是Android项目中一项比较棘手的问题,即便做了很多的try - catch处理,也不能保证上线不会崩,而且一旦出现崩溃,就
- 概述在Java环境下创建定时任务有多种方式:使用while循环配合 Thread.sleep(),虽然稍嫌粗陋但也勉强可用使用 Timer和