面试官: 有了解过指令重排吗,什么是happens-before

简介: 面试官: 有了解过指令重排吗,什么是happens-before

前言

目前正在出一个Java多线程专题长期系列教程,从入门到进阶含源码解读, 篇幅会较多, 喜欢的话,给个关注❤️ ~ 本篇内容篇纯理论一点


重排序

首先,什么是重排序❓计算机在执行过程中,为了提高性能,会对编译器和编译器做指令重排


这么做为啥可以提高性能呢❓


我们知道计算机在执行的时候都是一个个指令去执行,不同的指令可能操作的硬件不一样,在执行的过程中可能会产生中断,打个比方,两个指令a和b他们操作的东西各不相同,如果加载a的时候停顿了,b就加载不到,但是实际上它们互补影响,我也可以先加载b在加载a,所以指令重排是减少停顿的一种方法,这样大大提高了效率。


指令重排的方式

指令重排一般分为以下三种

  • 编译器优化 重新安排语句的执行顺序
  • 指令并行重排 利用指令级并行技术将多个指令并行执行,如果指令之前没有数据依赖,处理器可以改变对应机器指令的执行顺序
  • 内存系统重排 由于处理使用缓存和读写缓冲区,所以它们是乱序的


指令重排可以保证串行语义一致,但是没有义务保证多线程间的语义也一致**。所以在多线程下,指令重排序可能会导致一些问题


顺序一致性模型

顺序一致性模型是一个理论参考模型,内存模型在设计的时候都会以顺序一致性内存模型作为参考。


数据竞争

我们知道在多线程情况下,同时读写一个变量会导致结果的不确定性,这就存在了数据竞争,相反的如果线程在同步情况下,就不存在数据竞争。


JMM对于同步的多线程情况下,程序执行可以保证顺序一致性,同步包括了使用volatilefinalsynchronized等关键字来实现多线程下的同步,这里的前提正确使用它们,如果使用不当,就不能保证


什么是顺序一致性模型

我们在上节给大家讲了Java的内存模型,提到了内存可见性的概念,顺序一致性模型它的最终目的就是保证内存的可见性。


它主要有两大特性:

  • 一个线程中的所有操作必须按照程序的顺序(代码顺序)来执行。
  • 不管线程是否同步,所有线程保持单一的执行顺序并且可见,且是原子性


JMM中同步的顺序一致性

在JMM中,临界区(同步方法或同步块)的代码可以发生重排,但对其它线程是无感知的,这样既提高了执行效率又不影响最终结果


JMM中未同步的顺序一致性

  • JMM没有保证未同步程序的执行结果与该程序在顺序一致性中执行结果一致
  • JMM不保证单线程内的操作会按程序的顺序执行(因为指令重排)
  • JMM不保证所有线程能看到一致的操作执行顺序(因为不能保证所以操作立即可见)
  • JMM不保证对64位的long型和double型变量的写操作具有原子性


什么是happens-before

JMM提供了happens-before规则(JSR-133规范), 开发者可以遵循这种规范编写程序,可以保证程序在JMM中具有强的内存可见性。JMM使用happens-before的概念来定制两个操作之间的执行顺序。这两个操作可以在一个线程以内,也可以是不同的线程之间。

因此,JMM可以通过happens-before关系向程序员提供跨线程的内存可见性保证。


happens-before关系的定义如下:

  1. 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
  2. 两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须要按照happens-before关系指定的顺序来执行。如果重排序之后的执行结果,与按happens-before关系来执行的结果一致,那么JMM也允许这样的重排序。


总之,如果操作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规则:ThreadA start happens-before ThreadB start
  • join规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。


结束语

本节内容可能不像之前那么好理解,比较抽象,所以本文也有不足的地方,大家自己可以多查查一些资料,综合理解。下一节,带大家深入学习一下Java的volatile ~

相关文章
|
2月前
|
人工智能 前端开发
【ChatGPT 指令大全】怎么使用ChatGPT写履历和通过面试
【ChatGPT 指令大全】怎么使用ChatGPT写履历和通过面试
55 0
|
2月前
|
Java 编译器 程序员
【面试问题】什么是指令重排?
【1月更文挑战第27天】【面试问题】什么是指令重排?
|
2月前
|
Java 程序员
【面试问题】happens-before 是什么?
【1月更文挑战第27天】【面试问题】happens-before 是什么?
|
9月前
|
Java 编译器
【面试题精讲】JVM-clinit指令
【面试题精讲】JVM-clinit指令
|
10月前
|
缓存 JavaScript 前端开发
【Vue】模板语法,插值、指令、过滤器、计算属性及监听属性(内含面试题及毕设等实用案例)上篇
Vue 的模板语法是一种用于在 HTML 中声明式地渲染 Vue 组件的语法。它基于 HTML,并通过特定的模板语法扩展了 HTML。Vue 使用了一种称为 “Mustache” 语法的模板插值来绑定数据到 HTML 元素上。在 Vue 的模板语法中,你可以使用双大括号({{}})将数据绑定到 HTML 元素上,这样数据的值会被动态地替换到相应的位置。 在 Vue 的模板语法中,你可以使用双大括号({{}})将数据绑定到 HTML 元素上,这样数据的值会被动态地替换到相应的位置。
|
11月前
|
安全 Java
架构系列——面试必问:volatile的可见性、防止指令重排序以及不能保证原子性的解决方式
架构系列——面试必问:volatile的可见性、防止指令重排序以及不能保证原子性的解决方式
|
JavaScript 前端开发 Go
【面试系列一】如何回答如何理解重排和重绘
我发现很多候选人都没有答道关键点上,感觉是在哪里看到过相关的文章,听起来零零散散,毫无逻辑。
102 0
【面试系列一】如何回答如何理解重排和重绘
|
JavaScript 前端开发
|
缓存 Java 编译器
面试官:谈谈happens-before?
面试官:谈谈happens-before?
面试官:谈谈happens-before?
|
7天前
|
算法 Java 调度
《面试专题-----经典高频面试题收集四》解锁 Java 面试的关键:深度解析并发编程进阶篇高频经典面试题(第四篇)
《面试专题-----经典高频面试题收集四》解锁 Java 面试的关键:深度解析并发编程进阶篇高频经典面试题(第四篇)
15 0