【面试突击】硬件级别可见性问题面试实战(中)

🌈🌈🌈🌈🌈🌈🌈🌈
欢迎关注公众号(通过文章导读关注:【11来了】),及时收到 AI 前沿项目工具及新技术的推送!

在我后台回复 「资料」 可领取编程高频电子书
在我后台回复「面试」可领取硬核面试笔记

文章导读地址:点击查看文章导读!

感谢你的关注!

🍁🍁🍁🍁🍁🍁🍁🍁

经典指令重排案例

这里说一个在 JIT 动态编译时,经典的指令重排现象,双端检锁 时发生指令重排可能导致的错误

首先,双端检锁是用于构造单例对象的,如下:

public class Singleton {private static Singleton INSTANCE;private Singleton() {}public static Singleton getInstance() {//第一次校验单例对象是否为空if (INSTANCE == null) {//同步代码块synchronized (Singleton.class) {//第二次校验单例对象是否为空if (INSTANCE == null) {INSTANCE = new Singleton();}}}return INSTANCE;}public static void main(String[] args) {for (int i = 0; i < 20; i++) {new Thread(() -> System.out.println(Singleton.getInstance().hashCode())).start();}}
}

从字节码层面来看上述代码

 0 getstatic #2 <com/qy/nettychat/Volatile/Demo1.INSTANCE : Lcom/qy/nettychat/Volatile/Demo1;>3 ifnonnull 37 (+34)6 ldc #3 <com/qy/nettychat/Volatile/Demo1>8 dup9 astore_0
10 monitorenter
11 getstatic #2 <com/qy/nettychat/Volatile/Demo1.INSTANCE : Lcom/qy/nettychat/Volatile/Demo1;>
14 ifnonnull 27 (+13)
17 new #3 <com/qy/nettychat/Volatile/Demo1>
20 dup
21 invokespecial #4 <com/qy/nettychat/Volatile/Demo1.<init> : ()V>
24 putstatic #2 <com/qy/nettychat/Volatile/Demo1.INSTANCE : Lcom/qy/nettychat/Volatile/Demo1;>
27 aload_0
28 monitorexit
29 goto 37 (+8)
32 astore_1
33 aload_0
34 monitorexit
35 aload_1
36 athrow
37 getstatic #2 <com/qy/nettychat/Volatile/Demo1.INSTANCE : Lcom/qy/nettychat/Volatile/Demo1;>
40 areturn

其中双端检锁(DCL)部分字节码如下

17 new #3 <com/qy/nettychat/Volatile/Demo1>
20 dup
21 invokespecial #4 <com/qy/nettychat/Volatile/Demo1.<init> : ()V>
24 putstatic #2 <com/qy/nettychat/Volatile/Demo1.INSTANCE : Lcom/qy/nettychat/Volatile/Demo1;>
  • new 创建一个对象,并将其引用压入栈顶
  • dup 复制栈顶数值并将值压入栈顶
  • invokespecial 调用Demo1的初始化方法
  • putstatic 将该引用赋值给静态变量 INSTANCE

在单线程下 putstaticinvokespecial 进行指令重排,可以提高效率;在多线程下,指令重排可能会出现意想不到的结果

  • 单线程情况下,JVM 在执行字节码时,会出现指令重排情况:在执行完dup指令之后,为了加快程序执行效率,跳过构造方法的指令(invokespecial) ,直接执行putstatic指令,然后再将操作数栈上剩下的引用来执行invokespecial。单线程情况下JVM任何打乱invokespecialputstatic执行顺序并不会影响程序执行的正确性。
  • 多线程情况下,如果发生上述指令重排,此时第二个线程执行getInstance会执行到if(INSTANCE==NULL)此时会拿到一个尚未初始化完成的对象,那么使用未初始化完成的对象时可能会发生错误。

指令乱序机制

这个内容可以作为扩展了解!

指令乱序机制是现代处理器中用于提升性能的一种技术

指令乱序的意思时,处理器 不会按照程序中指令的顺序来严格顺序执行,而是会动态地调整指令地顺序,哪些指令先就绪,就先执行哪些指令,之后将每个指令的执行结果放到一个 重排序处理器 中,重排序处理器把各个指令的结果按照代码顺序应用到主内存或者写缓冲器里

指令乱序机制可能造成数据一致性的问题,因此处理器提供了 内存屏障同步原语(volatile、synchronized 等) 来保证在需要的时候,指令的执行顺序可以被保证同步,防止指令乱序执行

在这里插入图片描述

高速缓存和写缓冲器的内存重排序造成的视觉假象

这里讲一下高速缓存和写缓冲器的 内存重排序 造成的视觉假象:

处理器会将数据写入到写缓冲器中,这个过程就是 store;从高速缓存里读数据,这个过程就是 load,对于处理器来说,它的重排处理器是按照顺序来 load 和 stroe 的,但是如果在写缓冲器中发生了内存层面的指令重排序,就会导致其他处理器认为当前重排序后的指令顺序发生了变化

举个例子:比如现在有两个写操作 W1 和 W2,处理器先执行了 W1 再执行了 W2,写入到了 写缓冲器 中,而写缓冲可能为了提升性能,先将 W2 操作的数据写入到高速缓存中,再将 W1 操作的数据写入到高速缓存中,这样 W2 操作的结果先写入到 高速缓存 中后,会 先被其他处理器感知到,那么其他处理器就会误认为 W2 操作是先于 W1 操作执行的,这个就是 重排序造成的视觉假象

整个过程如下图,处理器 1 先执行 W1 再执行 W2,结果写缓冲器重排序后,将 W2 操作排在了前边:

在这里插入图片描述

这个内存重排序,有4种可能性:

  1. LoadLoad重排序:一个处理器先执行一个L1读操作,再执行一个L2读操作;但是另外一个处理器看到的是先L2再L1
  2. StoreStore重排序:一个处理器先执行一个W1写操作,再执行一个W2写操作;但是另外一个处理器看到的是先W2再W1
  3. LoadStore重排序:一个处理器先执行一个L1读操作,再执行一个W2写操作;但是另外一个处理器看到的是先W2再L1
  4. StoreLoad重排序:一个处理器先执行一个W1写操作,再执行一个L2读操作;但是另外一个处理器看到的是先L2再W1

接下来举一个具体的例子:

对于下边的代码,在硬件层面上可能多个线程并行地调度到不同地处理器上执行,通过并行执行来提高性能,假如 处理器 0处理器 1 同时来执行下边代码,如果处理器 0 的写缓冲器为了提高性能,进行了 内存重排序,先将 loaded = true 的结果更新到 高速缓存,再去更新 loadConfig() 的执行结果,那么如果处理器 0 刚更新完 loaded 的值,还没来得及更新 loadConfig 的值,此时 resource 还是 null,处理器 1 发现 loaded 为 true 了,直接调用 resource.execute() 方法,那么就会出现空指针的问题,这就是内存重排序可能会带来的问题

Config config = null;
Boolean loaded = false;// 处理器 0
resource = loadConfig();
loaded = true;// 处理器 1
while (!loaded) {try {Thread.sleep(1000);} catch (Exception e) {...}
}
resource.execute();

为了容易理解 指令重排如何造成空指针问题,我这里也画了一张时间线图:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/418442.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

有线桥接|Wifi隔了一堵墙就没信号?房间的网线口利用起来,让房间死角也有网!

前言 本篇文章是路由器有线桥接主路由&#xff0c;起到AP&#xff08;热点&#xff09;的效果 上次发布的无线桥接&#xff0c;使用的前提是需要把旧路由放置在主路由的信号范围内&#xff0c;这极大限制了桥接路由器的放置位置。 如果隔了一堵墙基本上就无法连接Wifi&#x…

STL——list

1、list介绍 1. list 是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. list 的底层是带头双向循环链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前一个元素和后…

Docker之网络配置的使用

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《Docker之网络配置的使用》。&#x1f3af;&…

Elasticsearch Index Shard Allocation 索引分片分配策略

Elasticsearch 索引分片的分配策略说明 在上一篇《索引生命周期管理ILM看完不懂你锤我 》&#xff08;https://mp.weixin.qq.com/s/ajhFp-xBU1dJm8a1dDdRQQ&#xff09;中&#xff0c;我们已经学会了索引级别的分片分配过滤属性&#xff0c;也就是在配置文件中指定当前节点的属…

springboot知识04

1、集成swaggershiro放行 &#xff08;1&#xff09;导包 &#xff08;2&#xff09;SwaggerConfig&#xff08;公共&#xff09; package com.smart.community.common.swagger.config;import io.swagger.annotations.ApiOperation; import org.springframework.beans.facto…

Java学习笔记(七)——操作数组工具类Arrays

文章目录 ArraysArrays.toString()Arrays.binarySearch()Arrays.copyOf()Arrays.copyOfRange()Arrays.fill()Arrays.sort()升序排序降序排序 Arrays 操作数组的工具类。 Arrays.toString() import java.util.Arrays;public class test40 {public static void main(String[] a…

python之粘包/粘包的解决方案

python之粘包/粘包的解决方案 什么是粘包 粘包就是在数据传输过程中有多个数据包被粘连在一起被发送或接受 服务端&#xff1a; import socket import struct# 创建Socket Socket socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 绑定服务器和端口号 servers_addr (…

Ubuntu系统pycharm以及annaconda的安装配置笔记以及问题集锦(更新中)

Ubuntu 22.04系统pycharm以及annaconda的安装配置笔记以及问题集锦 pycharm安装 安装完之后桌面上并没有生成图标 后面每次启动pycharm都要到它的安装路径下的bin文件夹下&#xff0c; cd Downloads/pycharm-2018.1.4/bin然后使用sh命令启动脚本程序来打开pycharm sh pycha…

【Linux的权限命令详解】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 shell命令以及运行原理 Linux权限的概念 Linux权限管理 一、什么是权限&#xff1f; 二、权限的本质 三、Linux中的用户 四、linux中文件的权限 4.1、文件访问…

Mybatis Plus baomidou EasyCode插件自动生成驼峰字段实体类,而不是全小写字段实体类

开发环境&#xff1a; springboot 2.4.3baomidou 3.4.0mybatis plus 3.4.0jdk8 问题描述&#xff1a; 1、mybatis 使用baomidou 插件&#xff0c;EasyCode自动生成实体类&#xff0c;但字段都是全部小写的&#xff0c;不太符合编码规范。 2、mysql表字段全是驼峰&#xff0c…

正则表达式第三四个作用:替换、切割

目录 方法二 replaceAll&#xff1a; 方法三&#xff1a;spilt&#xff1a; 方法一之前已经见过了&#xff1a; 方法二 replaceAll&#xff1a; 形参中&#xff1a; 参数regex表示一个正则表达式。可以将当前字符串中匹配regex正则表达式的字符串替换为newStr。 代码演示 S…

Windows给docker设置阿里源

windows环境搭建专栏&#x1f517;点击跳转 Windows系统的docker设置阿里源 文章目录 Windows系统的docker设置阿里源1.获得镜像加速器2.配置docker 由于我们生活在中国大陆&#xff0c;所以外网的访问总是那么慢又困难&#xff0c;用docker拉取几兆的小镜象还能忍受&#xff…