happens-before
happens-before是JMM最核心的概念。对应Java程序员来说,理解happens-before是理解JMM的关键。
JMM的设计
首先,让我们来看JMM的设计意图。从JMM设计者的角度,在设计JMM时,需要考虑两个关键因素。
- 程序员对内存模型的使用。程序员系统内存模型易于理解、易于编程。程序员希望基于一个强内存模型来编写代码。
- 编译器和处理器对内存模型的实现。编译器和处理器希望内存模型对它们的舒服越少越好,这样它们就可以尽可能多的优化来提高性能。编译器和处理器希望实现一个弱内存模型。
由于这两个因素互相矛盾,所以JSR-133专家组在设计JMM时的核心目标就是找到一个好的平衡点:一方面,要为程序员提供足够强大的内存可见性保证;另一方面,对编译器和处理器的限制要尽可能地放松。下面让我们来看JSR-133是如何实现这一目标的。
double pi = 3.14; //A
double r = 1.0; //B
double area = pi * r; //C
上面计算圆的面积的示例代码在3个happens-before关系,如下。
- A happens-before B。
- B happens-before C。
- A happens-before C。
在3个happens-before关系中,2和3是必需的,但1是不必要的。因此,JMM把happens-before要求禁止的重排序分为了下面两类。
- 会改变程序执行结果的重排序。
- 不会改变程序执行结果的重排序。
JMM对这两种不同性质的重排序,采取了不同的策略,如下。
- 对于会改变程序执行结果的重排序,JMM要求编译器和处理器必须禁止这种重排序。
- 对于不会改变程序执行结果的重排序,JMM对编译器和处理器不做要求(JMM允许这种重排序)。
如图是JMM的设计示意图:

从图中可以看出两点,如下:
- JMM向程序员提供的happens-before规则能满足程序员的需求。JMM的happens-before规则不但简单易懂,而且也向程序员提供了足够强的内存可见性保证
- JMM对编译器和处理器的束缚已经尽可能少。 从上面的分析可以看出,JMM其实是在遵循一个基本原则:只要不改变程序的执行结果(指的是单线程程序和正确同步的多线程程序),编译器和处理器怎么优化都行。
happens-before的定义
《JSS-131:Java Memory Model and Thread Specification》对happens-before关系的定义如下。
1)如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
2)两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须要按照happens-before关系指定的顺序来执行。
上面的1)是JMM对程序员的承诺
上面的2)是JMM对编译器和处理器重排序的约束原则
happens-before规则
《JSS-131:Java Memory Model and Thread Specification》定义了如下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()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B的任意操作。
6)join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。
这里的规则1)、2)3)和4)前面都讲到过,这里再做个总结。由于2)和3)情况类似,这里只以1)、3)和4)为例来说明。下图是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变量最后的写入。因此,volatile的这个特性可以保证实现volatile规则。
- 1 happens-before 4是由传递性规则产生的。这里的传递性是由volatile的内存屏障插入策略和volatile的编译器重排序规则共同来保证的。
下面我们来看start()规则。假设线程A在执行的过程中,通过执行ThreadB.start()来启动线程B;同时,假设线程A在执行ThreadB.start()之前修改了一些共享变量,线程B在开始执行后会读这些共享变量。下图是该程序对应的happens-before关系图。

上图中,1 happens-before 2由程序顺序规则产生。2 happens-before 4由start()规则产生。根据传递性,将有1 happens-before 4。这意味着,线程A在执行ThreadB.start()前共享变量所做的修改,接下来在线程B开始执行后都将确保对线程B可见。
下面来看看join()规则。假设线程A在执行的过程中,通过执行ThreadB.join()来等待线程B终止;同时,假设线程B在终止之前修改了一些共享变量,线程A从ThreadB.join()返回后会读这些共享变量。下图是该程序对应的happens-before关系图。

上图中,2 happens-before 4有join()规则产生;4 happens-before 5由程序顺序规则产生。根据传递性规则,将有2 happens-before 5。这意味着,线程A执行ThreadB.join()并成功返回后,线程B中的任意操作都将对线程A可见。
随机推荐
- Ionic 入门与实战之第三章:Ionic 项目结构以及路由配置
原文发表于我的技术博客 本文是「Ionic 入门与实战」系列连载的第三章,主要对 Ionic 的项目结构作了介绍,并讲解了Ionic 中的路由概念以及相关配置. 原文发表于我的技术博客 1. Ioni ...
- Android环境准备
Android环境准备: 1.安装Java环境(自行安装) 2.下载Android SDK包 3.配置系统环境变量 新建ANDROID_HOME 变量值:SDK安装路径(如:D:\android-sd ...
- Linux下rsyslog日志收集服务环境部署记录
rsyslog 可以理解为多线程增强版的syslog. 在syslog的基础上扩展了很多其他功能,如数据库支持(MySQL.PostgreSQL.Oracle等).日志内容筛选.定义日志格式模板等.目 ...
- linux上启动tomcat远程不能访问
linux上关闭防火墙同样访问不了,执行iptables -f即可. 你试一试这个“iptables -F”然后再访问,如果能够访问了,那么需要执行“firewall-cmd --add-port=8 ...
- linux及安全第三周总结——跟踪分析LINUX内核的启动过程
linux内核目录结构 arch目录包括了所有和体系结构相关的核心代码.它下面的每一个子目录都代表一种Linux支持的体系结构,例如i386就是Intel CPU及与之相兼容体系结构的子目录.PC机一 ...
- 软件工程(五)UML
UML 统一建模语言,又称标准建模语言.是用来对软件密集系统进行可视化建模的一种语言.包括UML语义和UML表示法两个元素. UMl图由事物和关系组成,事物:UML模型中最基本的构成元素,是具有代表性 ...
- Leetcode——171.Excel表列序号【水题】
@author: ZZQ @software: PyCharm @file: leetcode171_Excel表列序号.py @time: 2018/11/22 15:29 要求: 给定一个Exce ...
- 小学四则运算APP 最后阶段
团队成员:陈淑筠.杨家安.陈曦 团队选题:小学四则运算APP 这次发布的是我们APP的最终版本!图片背景有根据用户需求改变!还增加了草稿纸运算的画布功能! 运行结果如下: package com.ex ...
- beta 答辩总结
评审表 编号 团队名称 项目名称 格式 内容 PPT 演讲 答辩 总分/100 1 天机组 指尖加密 14 13 13 13 13 66 2 PMS Your eyes 14 14 17 14 15 ...
- Expanded encryption and decryption signature algorithm SM2 & SM3
Expanded encryption and decryption signature algorithm supports multiple signature digest algorithms ...