我们一起学并发编程:Java内存模型七happens-before

分析上图:2happens-before4由join()规则产生 4happens-before5由程序顺序规则产生 2happens-before5由传递性规则产生 因此线程A执行操作ThreadB.join()并成功返回,线程B中任意操作都将对线程A可见。文章总结至《Java并发编程艺术》,下篇总结“双重检查所定与延迟初始化”,敬请关注。
我们一起学并发编程:Java内存模型七happens-before
简介happens-before是JMM的核心概念,理解happens-before是了解JMM的关键。
1、设计意图JMM的设计需要考虑两个方面,分别是程序员角度和编译器、处理器角度:
程序员角度,希望内存模型易于理解、易于编程。希望是一个强内存模型。
编译器和处理器角度,希望减少对它们的束缚,以至于编译器和处理器可以做更多的性能优化。希望是一个弱内存模型。
因此JSR-133专家组设计JMM的核心目标就两个:
为程序员提供足够强的内存模型
对编译器和处理器的限制尽可能少
下面通过一段代码来看JSR-133如何实现这两个目标:
doublepi=3.14;//Adoubler=1.0;//Bdoublearea=pi*r*r//C上述代码存在如下happens-before关系:
Ahappens-beforeB
Bhappens-beforeC
Ahappens-beforeC
这3个happens-before关系中,第二个和第三个是必须的,而第一个是非必须的(A、B操作之间重排序,程序执行结果不会发生改变)。
JMM把happens-before要求禁止的重排序分为下面的两类:
会改变程序执行结果的重排序
不会改变程序执行结果的重排序
JMM对这两种不同性质的重排序,采取了不同的策略:
对于会改变程序执行结果的重排序,JMM要求编译器和处理器必须禁止
对于不会改变程序执行结果的重排序,JMM不做要求(JMM运行)
JMM设计示意图:

JMM设计示意图
总结:
JMM给程序员提供的happens-before规则能满足程序员的需求。简单易懂,具有足够强的内存可见性保证。
JMM对编译器和处理器的束缚尽可能少。遵循的原则是:不改变程序的执行结果(正确同步或单线程执行),编译器和处理器可以任意优化。
2、happens-before的定义起源:
happens-before规则来源于LeslieLamport《Time,ClocksandtheOrderingofEventsinaDistributedSystem》。该论文中使用happens-before来定义分布式系统中事件之间的偏序关系(partialordering),该文中给出了一个分布式算法,能用来将偏序关系扩展为某种全序关系。
Java中的应用:
JSR-133使用happens-before来指定两个操作之间的执行顺序。JMM可以通过happens-before关系向程序员提供跨线程的内存可见性保证。
《JSR-133:JavaMemoryModelandThreadSpecification》对happens-before关系的定义如下:
如果操作Ahappens-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:JavaMemoryModelandThreadSpecification》定义了如下happens-before规则
程序顺序规则
监视器锁规则
volatile变量规则
传递性
start()规则
join()规则
3.1volatile写-读volatile写-读建立的happens-before关系

happens-before关系示意图
分析上图:
1happens-before2和3happens-before4由程序顺序规则产生。由于编译器和处理器遵循as-if-serial语义,也就是说,as-if-serial语义保证了程序顺序规则。因此可以把程序顺序规则看成是对as-if-serial语义的“封装”。
2happens-before3是有volatile规则产生。一个volatile变量的读,总是能看到(任意线程)对这个volatile变量的最后写入。
1happens-before4是由传递性规则产生的。这里的传递性是由volatile的内存屏障插入策略和volatile的编译器重排序规则来共同保证的。
3.2start()规则假设线程A在执行的过程中,通过执行ThreadB.start()来启动线程B;同时,假设线程A在执行ThreadB.start()之前修改了一个共享变量,线程B在执行后会读取这些共享变量。
start()程序对应的happens-before关系图:

分析上图:
1happens-before2由程序顺序规则产生
2happens-before4由start规则产生
1happens-before4由传递性规则产生
因此线程A执行ThreadB.start()之前对共享变量所做的修改,在线程B执行后都将确保对线程B可见。
3.3join()规则假设线程A执行的过程中,通过执行ThreadB.join()来等待线程B终止;则线程B在终止之前修改了一些共享变量,线程A从ThreadB.join()返回后会读这些共享变量。
join()程序的happens-before关系图:

分析上图:
2happens-before4由join()规则产生
4happens-before5由程序顺序规则产生
2happens-before5由传递性规则产生
因此线程A执行操作ThreadB.join()并成功返回,线程B中任意操作都将对线程A可见。
文章总结至《Java并发编程艺术》,下篇总结“双重检查所定与延迟初始化”,敬请关注。
2024-09-19
mengvlog 阅读 12 次 更新于 2025-06-20 00:40:17 我来答关注问题0
  • 一、Java内存模型的基础1.1并发编程模型的两个关键问题 在并发编程中需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体)。 通信——线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。 共享内存:线程之间共享程序的公共状态,通过读...

  • 我们一起学并发编程:Java内存模型(三)顺序一致性简介:顺序一致性内存模型是一个理论参考模型,处理器的内存模型和编程语言的内存模型都会以顺序一致性内存模型作为参照。1、数据竞争和顺序一致性当程序未正确同

  •  猪八戒网 我们一起学并发编程:Java内存模型(八)双重检查锁定与延迟初始化

    在Java语言规范中,首次发生如下情况中的任意一种,一个类或者一个接口类型T将会被立即初始化: T是一个类,而且一个T类型的实例被创建 T是一个类,且T中声明的一个静态方法被调用 T中声明的一个静态字段被赋值 T中声明的一个静态字段被使用,而且这个字段不是一个常量字段 T是一个顶级类(TopLevelClass),而且一...

  •  文暄生活科普 CAS底层原理示例+详解,终于弄清楚了!

    大家好,今天咱们一起探讨CAS(Compare-And-Swap)这个并发编程中的核心概念。CAS是一种CPU原语,主要作用是在内存中判断某个位置的值是否为预期值,若相符则进行更新,整个过程在硬件层面保证原子性,避免数据不一致,从而实现线程安全。在Java中,sun.misc.Unsafe类提供了实现CAS的接口,JVM会通过底层的...

  •  ya...0@163.com java学习路线

    Java学习路线一般有以下几个阶段:第一阶段,JavaSE基础:Java环境搭建、Java流程控制语句-for循环、switch选择判断、循环嵌套、数组拷贝等。第二阶段,JavaWeb:MySQL安装、管理、创建数据库、MySQLUPDATE查询、Mysql高级操作等。第三阶段,Java高级框架-SSH:Struts2异常处理、Struts2+Log4j集成、Struts2和...

檬味博客在线解答立即免费咨询

编程相关话题

Copyright © 2023 WWW.MENGVLOG.COM - 檬味博客
返回顶部