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

如题所述

第1个回答  2024-09-19
简介

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并发编程艺术》,下篇总结“双重检查所定与延迟初始化”,敬请关注。

logo设计

创造品牌价值

¥500元起

APP开发

量身定制,源码交付

¥2000元起

商标注册

一个好品牌从商标开始

¥1480元起

公司注册

注册公司全程代办

¥0元起

    官方电话官方服务
      官方网站八戒财税知识产权八戒服务商企业需求数字市场

我们一起学并发编程:Java内存模型(七)happens-before
两个操作存在happens-before关系,并不意味着Java平台的具体实现必须按照happens-before的顺序来执行。如果重排序不改变程序执行结果(与happens-before)规则一致,那么这种重排序是不非法的(JMM允许这种重排序)。——JMM对编译器和处理器的束缚原则 happens-before和as-if-serial语义:从上述来看,happens-...

什么是 happens-before 规则?
什么是 happens-before 规则Happens-before关系是一种在并发编程中描述可见性问题的关键概念。它定义了一个操作对另一个操作的顺序,确保第一个操作的结果在第二个操作中是可见的。当操作 A happens-before 操作 B,意味着操作 B在执行时,必然能观察到操作 A的影响。举个例子,考虑两个线程,如果线程...

JMM与顺序一致模型和happens-before模型的关系和区别
原始的happens-before模型太弱了,所有Java内存模型允许的行为,happens-before内存模型也允许,但是有些行为是Java内存模型不允许的,比如以不可捉摸的方法违反因果关系——允许某些值凭空出现。如下图一: 如上代码,在正确同步的原始的happens-before内存模型中,存在执行结果是r1=r2=1的情况。因为在原始的happens-before内...

05.深入理解JMM和Happens-Before
Java内存模型定义了多线程环境下对内存操作的可见性和同步性规则,而Happens-Before则描述了操作间的因果关系,确保并发环境下程序的正确执行。在多线程编程中,线程调度、缓存和指令重排序等底层因素可能导致并发问题,如原子性、可见性和有序性问题。JMM通过一系列规则和原语(如synchronized、volatile和final...

8大原则带你秒懂Happens-Before原则
接下来,我们就结合案例程序来说明Java内存模型中的Happens-Before原则。【原则一】程序次序规则在一个线程中,按照代码的顺序,前面的操作Happens-Before于后面的任意操作。例如【示例一】中的程序x=42会在v=true之前执行。这个规则比较符合单线程的思维:在同一个线程中,程序在前面对某个变量的修改一定...

8大原则带你秒懂Happens-Before原则
结合【示例一】程序,我们可以得出x = 42 Happens-Before写变量v = true,以及写变量v = true Happens-Before读变量v = true,进而推断x = 42 Happens-Before读变量v = true。这说明线程B读取到的变量v为true时,线程A设置的x = 42对线程B是可见的。Java 1.5版本的并发工具利用volatile语义实现...

并发的三大特性&Java内存模型
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。Java内存模型还规定了在执行上述八种基本操作时,必须满足happens-before原则。

我们一起学并发编程:Java内存模型(五)锁的内存语义
我们一起学并发编程:Java内存模型(五)锁的内存语义简介:锁的作用是让临界区互斥执行。本文阐述所得另一个重要知识点——锁的内存语义。1、锁的释放-获取建立的happens-before关系锁是Java并发编程中最重要的同步

一文详解JMM(Java 内存模型)
JMM(Java内存模型)抽象了线程与主内存的关系,定义了源代码到可执行指令的转化过程中的并发原则和规范,简化多线程编程。JMM通过happens-before原则解决指令重排序导致的并发问题,确保共享变量的可见性。JSR-133引入了此概念。了解happens-before原则的目的是实现内存可见性,确保操作结果对后续操作可见。掌握...

2020-11-12:java中as-if-serial语义和happen-before语义有什么区别...
happens-before关系本质上和as-if-serial语义是一回事。这个是《JAVA并发编程的艺术》里的原话。

相似回答
大家正在搜