重排序和happens-before有什么关系

技术重排序和happens-before有什么关系本篇内容介绍了“重排序和happens-before有什么关系”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情

本文介绍了“重新排序和发生之前的关系是什么”的知识。很多人在实际案例的操作中会遇到这样的困难。让边肖带领你学习如何处理这些情况。希望大家认真阅读,学点东西!

举个例子

在谈论重新排序之前,让我们看一个例子:

inta=0,b=0;public void methodone(){ intone=a;b=1;} public void method two(){ int two=b;a=2;}应该不难看出,在上面的例子中,我定义了两个共享变量a和b,以及两个方法。第一种方法是将局部变量1赋给a,然后将b的值设置为1。第二种方法是将局部变量2赋给B,然后将A的值设置为2。

那么我这里有一个问题。(一,二)的价值是什么?

你可能会不假思索地告诉我它不是(0,1)就是(2,0),这取决于我的主方法先执行哪个方法。

是的,如果程序在单线程上运行,这个答案没有错。

但是如果它在多线程环境中呢?

假设方法一和方法二在两个不同的线程上执行。此时,Java虚拟机在执行任何方法的第一个赋值语句后切换线程。此时,(一,二)的值可能是(0,0)。

看到这里,你有什么疑惑吗?为什么,为什么我写的程序到了Java虚拟机,它就为我改变了?

因为在执行的过程中,发生了重新排序。它可能是及时编译器的重新排序、处理器的无序执行或内存系统的重新排序。

总之,在程序执行过程中,会发生重新排序,然后结果可能是(0,0)。

为什么会重排序

看完上面,你可能会奇怪为什么会有重新排序。

我的程序是按照我自己的逻辑写的,所以没有问题。为什么Java虚拟机会移动我的程序逻辑?

想想看,CPU和内存都是非常宝贵的资源。如果Java虚拟机重新排序后没有效果,肯定不会做这种吃力不讨好的事情。

那么,重新排序有什么好处呢?

重排序使得程序的性能得以提高

为了方便理解,我以生活中的场景为例。

当你早上起床的时候,你会穿衣、洗漱、做饭和吃饭,对吗?你起床后做了什么?洗的时候是不是先把饭煮好(比如让蒸蛋器给你蒸一个鸡蛋),然后洗完就可以直接吃早餐了。

你为什么要这么做?不仅仅是为了节省时间,你可以多睡一分钟,对吗?

同理,重新排序Java虚拟机的原因也是为了提高程序的性能。你写的程序很简单,只有一行代码,你可能需要在底层使用不同的硬件。比如一条指令需要同时使用CPU和打印机设备,但是此时CPU的任务完成了,但是打印机的任务还没有完成。这个时候该怎么办?不让CPU执行下一条指令?CPU的时间太宝贵了,你不让它工作。你确定你没有浪费它的生命吗?

因此,为了提高程序的利用率和性能,Java虚拟机会在您完全执行完这条指令之前执行另一条指令。这就是管道技术

流水线最怕什么?我正在执行命令,执行命令,突然被打断了。恢复中断的成本非常高,所以我们必须尽最大努力绞尽脑汁,以免中断发生。

实时编译器的重新排序、处理器的无序执行和内存系统的重新排序都是为了减少中断。

此时,您是否熟悉Java虚拟机的重新排序?

重排序带来的问题

回到文章开头给出的例子,重新排序确实提高了CPU利用率和程序性能,但是我的程序得到的结果可能是错误的。这是不是有点得不偿失?

因为重排序可以保证串行语义一致性,所以没有义务保证多线程之间的语义一致性。

每一个问题都可以解决。如果没有,再想想。

怎么解决?有必要谈谈顺序一致内存模型和JMM (Java Memory Model)。

顺序一致性内存模型与 JMM

如果要谈数据一致性,就必须谈数据竞争。

什么是数据竞争?

在 Java 内存模型规范中给出了定义:

  • 在一个线程中写一个变量

  • 在另外一个线程中读同一个变量

  • 写和读没有通过同步来排序

当代码中包含数据竞争时,程序的执行结果往往会超出你的想象,比如咱们刚开始说的那个例子,得到的结果可能是 ( 0 , 0 )  。但是如果一个多线程程序能够正确同步的话,那上面的结果就不会出现了。

Java 内存模型对于正确同步多线程程序的内存一致性做了下面的保证:

如果程序是正确同步的,程序的执行也会具有顺序一致性即,程序的执行结果与该程序在顺序一致性模型中执行的结果相同

这里面的同步包括了使用 volatile , final , synchronized  等关键字来实现多线程下的同步。那也就是说,如果没有正确使用这些同步, JMM 就不会有内存可见性的保证,这就会导致写的程序出错。

顺序一致性内存模型是一个理想状态下的理论参考模型,它为程序员提供了特别强的内存可见性保证,顺序一致性模型有两大特性:

  • 一个线程中的所有操作必须按照程序的顺序来执行(也就是按照写的代码的顺序来执行)

  • 不管程序是否同步,所有线程都只能看到一个单一的操作执行顺序。也就是说,在顺序一致性模型中,每个操作必须是原子性的,而且立刻对所有线程都是可见的。

上面说了,顺序一致性内存模型是一个理想状态下的理论参考模型,因为顺序一致性内存模型要求操作对所有线程都是可见,只是这一点就会让 Java  虚拟机的性能降低。JMM 就是在顺序一致性内存模型的基础上,做了一些优化:

  • 针对同步的多线程程序来说,也就是临界区内的代码, JMM  允许发生重排序(但是不允许临界区内的代码"逃逸"到临界区之外,因为如果允许的话,就会破坏锁的内存语义)

  • 针对未同步的多线程程序来说, JMM 只提供最小安全性:线程读取到的值,要么是之前某个线程写入的值,要么是默认值,不会无中生有。

应该能够感觉到,相比于顺序一致性内存模型来说, JMM 给了编译器和处理器一些空间,允许它们发生重排序。

这时候就有冲突点了:程序员这边需要 JMM  提供一个强的内存模型来编写代码,也就是我代码写的顺序是什么样,那程序执行的时候就要是什么样;但是编译器和处理器则需要 JMM  对它们的约束越少越好,这样它们就可以尽可能多的去做优化,来提高性能

作为 JMM  这个中介者来说,既要满足程序员的需求,又要满足编译器和处理器的需求,那就需要在这两者之间找一个平衡点,让程序员写的代码能够产生他期望的结果,同时呢,也让编译器和处理器能够做一些优化

JMM 提出的解决方案就是:对于程序员,提供 happens-before 规则,这样就满足了程序员的需求 --->  简单易懂,而且提供了足够强的内存可见性保证;对于编译器和处理器来说,只要不改变程序的执行结果(前提是正确同步了多线程程序),想怎么优化就怎么优化。

happens-before

终于讲到了 happens-before 。

先来看 happens-before 关系的定义:

  • 如果一个操作 happens-before 另一个操作,那么第一个操作的执行结果就会对第二个操作可见

  • 两个操作之间如果存在 happens-before 关系,并不意味着 Java 平台的具体实现就必须按照 happens-before  关系指定的顺序来执行。如果重排序之后的执行结果,与按照 happens-before 关系来执行的结果一直,那么 JMM 也允许这样的重排序

看到这儿,你是不是觉得,这个怎么和 as-if-serial 语义一样呢。没错, happens-before 关系本质上和 as-if-serial  语义是一回事。

as-if-serial 语义保证的是单线程内重排序之后的执行结果和程序代码本身应该出现的结果是一致的, happens-before  关系保证的是正确同步的多线程程序的执行结果不会被重排序改变。

一句话来总结就是:如果操作 A happens-before 操作 B ,那么操作 A 在内存上所做的操作对操作 B  都是可见的,不管它们在不在一个线程。

在 Java 中,对于 happens-before 关系,有以下规定:

  • 程序顺序规则:一个线程中的每一个操作, happens-before 于该线程中的任意后续操作

  • 监视器锁规则:对一个锁的解锁, happens-before 于随后对这个锁的加锁

  • volatile 变量规则:对一个 volatile 域的写, happens-before 与任意后续对这个 volatile 域的读

  • 传递性:如果 A happens-before B , 且 B happens-before C ,那么 A happens-before C

  • start 规则:如果线程 A 执行操作 ThreadB。start() 启动线程 B ,那么 A 线程的 ThreadB。start() 操作  happens-before 于线程 B 中的任意操作

  • join 规则:如果线程 A 执行操作 ThreadB。join() 并成功返回,那么线程 B 中的任意操作 happens-before 于线程 A 从  ThreadB。join() 操作成功返回。

“重排序和happens-before有什么关系”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/65487.html

(0)

相关推荐

  • animation

    技术animation animationanimation一般是与 @keyframes一起用,来制作网页动画。但animation有很多参数,每种参数对应的属性有不同含义。参数类型如下:animat

    礼包 2021年11月22日
  • excel表格怎么设置下拉选项,excel表格中如何增加下拉

    技术excel表格怎么设置下拉选项,excel表格中如何增加下拉1、首先打开excelexcel表格怎么设置下拉选项,新建空白工作簿,并添加要显示在下拉列表中的条目。注意:这些条目应当位于同一列或同一行中且没有空白单元格

    生活 2021年10月19日
  • Spring中spring-context-indexer依赖有什么用

    技术Spring中spring-context-indexer依赖有什么用这篇文章给大家分享的是有关Spring中spring-context-indexer依赖有什么用的内容。小编觉得挺实用的,因此分享给大家做个参考,

    攻略 2021年11月9日
  • 复杂的数据需求的MySQL方案是怎样的

    技术复杂的数据需求的MySQL方案是怎样的今天就跟大家聊聊有关复杂的数据需求的MySQL方案是怎样的,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。 前些天处理

    攻略 2021年11月16日
  • javascript如何将对象转化为数组

    技术javascript如何将对象转化为数组这篇文章将为大家详细讲解有关javascript如何将对象转化为数组,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

    攻略 2021年11月19日
  • Python爬取世纪佳缘的数据是否能证明它不靠谱的示例分析

    技术Python爬取世纪佳缘的数据是否能证明它不靠谱的示例分析这期内容当中小编将会给大家带来有关Python爬取世纪佳缘的数据是否能证明它不靠谱的示例分析,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大

    攻略 2021年10月26日