重排序问题

【Java并发编程之什么是指令重排序?如何避免指令重排序?】 在Java中,指令重排序是指编译器和处理器为了提高程序执行效率,可能会对指令的执行顺序进行重新排列。这种重排序不会改变单线程程序的执行结果(即遵循as-if-serial语义),但在多线程环境下,可能会导致不可预期的行为。 阅读详情

重排序问题

volitile和synchronize

volitile可以保证可见性,不能保证原子性,禁止重排序

synchronize:既然可以保证可见性又能 保证原子性

 

重排序:

cpu对代码实现优化,不会对有依赖关系的做重排序(多线程情况下)

多线程情况下是没有影响的

数据依赖性

如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。数据依赖分下列三种类型:

名称

代码示例

说明

写后读

a = 1;b = a;

写一个变量之后,再读这个位置。

写后写

a = 1;a = 2;

写一个变量之后,再写这个变量。

读后写

a = b;b = 1;

读一个变量之后,再写这个变量。

 

上面三种情况,只要重排序两个操作的执行顺序,程序的执行结果将会被改变。

前面提到过,编译器和处理器可能会对操作做重排序。编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。

注意,这里所说的数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作,不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑。 

int a = 1;

int b =2;

int c = a*b;

a和b没有关系 c和a、b都有关系。 cpu会对a和b做重排序。正常情况限制性a再b 。但是可能会先执行b

但是这个例子对结果是没有影响的

as-if-serial语义

s-if-serial语义的意思指:不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变。编译器,runtime 和处理器都必须遵守as-if-serial语义。

为了遵守as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。但是,如果操作之间不存在数据依赖关系,这些操作可能被编译器和处理器重排序。为了具体说明,请看下面计算圆面积的代码示例:

double pi  = 3.14;    //A

double r   = 1.0;     //B

double area = pi * r * r; //C

上面三个操作的数据依赖关系如下图所示:

 

如上图所示,A和C之间存在数据依赖关系,同时B和C之间也存在数据依赖关系。因此在最终执行的指令序列中,C不能被重排序到A和B的前面(C排到A和B的前面,程序的结果将会被改变)。但A和B之间没有数据依赖关系,编译器和处理器可以重排序A和B之间的执行顺序。下图是该程序的两种执行顺序:

 

as-if-serial语义把单线程程序保护了起来,遵守as-if-serial语义的编译器,runtime 和处理器共同为编写单线程程序的程序员创建了一个幻觉:单线程程序是按程序的顺序来执行的。as-if-serial语义使单线程程序员无需担心重排序会干扰他们,也无需担心内存可见性问题。

程序顺序规则

根据happens- before的程序顺序规则,上面计算圆的面积的示例代码存在三个happens- before关系:

1.     A happens- before B;

2.     B happens- before C;

3.     A happens- before C;

这里的第3个happens- before关系,是根据happens- before的传递性推导出来的。

这里A happens- before B,但实际执行时B却可以排在A之前执行(看上面的重排序后的执行顺序)。在第一章提到过,如果A happens- before B,JMM并不要求A一定要在B之前执行。JMM仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前。这里操作A的执行结果不需要对操作B可见;而且重排序操作A和操作B后的执行结果,与操作A和操作B按happens- before顺序执行的结果一致。在这种情况下,JMM会认为这种重排序并不非法(not illegal),JMM允许这种重排序。

在计算机中,软件技术和硬件技术有一个共同的目标:在不改变程序执行结果的前提下,尽可能的开发并行度。编译器和处理器遵从这一目标,从happens- before的定义我们可以看出,JMM同样遵从这一目标。

 

serial不管怎么去做重排序,但是目的提高并发度,但是不能影响到正常的结果。

重排序,是多线程遇到的问题。

看看这个例子:

class recordingDemo{
    
    int a = 0;
    boolean flag = false;
    
     //写入线程
    public void writer(){
        a =1;    //这两行代码没有依赖关系 则会做重排序   万一先执行 falg=true呢?
        flag=true;
    }
    //读取线程
    public void reader(){
        if (flag) {
            int i = a*a;
        }
    }
    
}

当然只是多线程时候会有这样的情况,单线程时候代码从上到下运行,不会出现重排序问题的

于是乎volitile关键字又来了

synchronize没有重排序功能哈~

重排序的结果例子很难模拟出来的,所以大家要深入去理解

 

flag变量是个标记,用来标识变量a是否已被写入。这里假设有两个线程A和B,A首先执行writer()方法,随后B线程接着执行reader()方法。线程B在执行操作4时,能否看到线程A在操作1对共享变量a的写入?

答案是:不一定能看到。

由于操作1和操作2没有数据依赖关系,编译器和处理器可以对这两个操作重排序;同样,操作3和操作4没有数据依赖关系,编译器和处理器也可以对这两个操作重排序。让我们先来看看,当操作1和操作2重排序时,可能会产生什么效果?请看下面的程序执行时序图:

 

如上图所示,操作1和操作2做了重排序。程序执行时,线程A首先写标记变量flag,随后线程B读这个变量。由于条件判断为真,线程B将读取变量a。此时,变量a还根本没有被线程A写入,在这里多线程程序的语义被重排序破坏了!

※注:本文统一用红色的虚箭线表示错误的读操作,用绿色的虚箭线表示正确的读操作。

下面再让我们看看,当操作3和操作4重排序时会产生什么效果(借助这个重排序,可以顺便说明控制依赖性)。下面是操作3和操作4重排序后,程序的执行时序图:

 

在程序中,操作3和操作4存在控制依赖关系。当代码中存在控制依赖性时,会影响指令序列执行的并行度。为此,编译器和处理器会采用猜测(Speculation)执行来克服控制相关性对并行度的影响。以处理器的猜测执行为例,执行线程B的处理器可以提前读取并计算a*a,然后把计算结果临时保存到一个名为重排序缓冲(reorder buffer ROB)的硬件缓存中。当接下来操作3的条件判断为真时,就把该计算结果写入变量i中。

从图中我们可以看出,猜测执行实质上对操作3和4做了重排序。重排序在这里破坏了多线程程序的语义!

在单线程程序中,对存在控制依赖的操作重排序,不会改变执行结果(这也是as-if-serial语义允许对存在控制依赖的操作做重排序的原因);但在多线程程序中,对存在控制依赖的操作重排序,可能会改变程序的执行结果。

 

posted @ 2018-10-22 18:13 toov5 阅读( ...) 评论( ...) 编辑 收藏
多线程中的指令重排序问题 java并发编程中的重排序问题 重排序 在多核处理器的环境下,编写的顺序结构,这种操作执行的顺序可能是没有保障的: 编译器、处理器可能会改变两个操作的先后顺序 这种一个处理器上执行的多个操作,在其他处理器来看它的顺序与目标代码指定的顺序可能是不一样的,这种现象称为重排序。 重排序是对内存访问有序操作的一种优化,可以在不影响单线程程序正确的情况下提升程序的性能,但是,可能对多线程程序的正确性产生影响,即可能产生线程安全问题。 重排序与可见性问题类似,不是必然出现的,与内存操作顺序有关的几个概念: 源代码执行 阅读详情

相关推荐

Arcgis填补栅格空缺值Nodata

Arcgis填补栅格空缺值Nodata

pass17的博客 1万+

指令重排序

2.3. 指令重排序 2.3.1. 指令重排序的介绍 1)指令重排序的类型 在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。重排序分三种类型: 编译器优化的重排序 编译器在不改变单线程程序语义的前提下(代码中不包含synchronized关键字),可以重新安排语句的执行顺序。 指令级并行的重排序 现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。 内存系统的

Waiting_Mr_Liu的博客 5936

CentOS 7 安装 Python 3.10

我这里已经软链接了,正常未链接之前是python->python2.7而不是python3.10。python2.7默认安装是在 /usr/bin目录中,切换到/usr/bin/更改yum配置,因为其要用到python2才能执行,否则会导致yum不能正常使用。2. 安装 Python 3.10 必需的 openssl >= 1.1.1。默认的centos7是没有安装pip,先添加epel扩展源。/usr/bin/python 修改为如下。添加软链接,将原来的链接备份。添加python3的软链接。

bensonyang888的博客 2905

Java并发编程之指令重排序

在我们面试过程中,通常避免不了会被问到什么是指令重排序?本文就这个问题进行探索。 重排序前言一、重排序种类二、happens-before三、重排序1.数据依赖性2. as-if-serial语义3.程序顺序规则4.重排序在多线程中的影响 前言 在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序。 一、重排序种类 在java语言中,重排序分为3种。 编译器优化的重排序。编译器在不改变单线程程序的语义前提下,可以重新安排语句的执行顺序。 指令级并行的重排序。现在处理器采用了指令集并行技术,

weixin_44692700的博客 4856

计算机模拟爆破过程,基于LSDYNA岩石爆破模拟建模分析

原标题:基于LSDYNA岩石爆破模拟建模分析 Ls Dyna岩石爆破模拟仿真作者:dyna_focus擅长领域:dyna/abaqus/hypermesh一、 数值模型的建立1.1 单元及算法的选择岩石,炸药,空气都采用solid164实体单元。岩石采用常应力实体单元,该种单元属于纯粹的lagrange算法,该种算法单元网格附着在材料上,随着材料的流动而产生单元网格的变形,但是在结构变形巨大时,有...

weixin_29494379的博客 7316

【02】【VisionMaster V4.0.0 】安装教程—— 授权文件更新报错:应用程序与本地LM通讯出错(怀疑电脑加密 和 上网受限 造成的 )

本文详细介绍了海康VisionMaster 4.0试用版的安装流程及常见问题解决方案。主要内容包括:1)从官网下载基础安装包、试用补丁和授权文件;2)按顺序安装主程序、补丁和授权文件;3)针对授权失败问题提供两种解决方案(更换未加密电脑或使用加密狗);4)说明试用版的功能限制(30天试用期、2个流程限制等)。文章特别指出电脑加密可能导致授权失败,并提供了安装报错时的处理建议,为初次使用者提供了完整的安装指导。

程序员,他们想的是什么?他们想的永远都是技术,他们崇尚的也永远都是技术。 7171

Java多线程 -- 深入理解JMM(Java内存模型) --(二)重排序

在单线程程序中,对存在控制依赖的操作重排序,不会改变执行结果(这也是as-if-serial语义允许对存在控制依赖的操作做重排序的原因);但在多线程程序中,对存在控制依赖的操作重排序,可能会改变程序的执行结果。

NowOrNever 7936

Julia Ann老司机管理系统

https://content.badgirlsusa.com/Julia_Ann-Sexy_Julia_Ann_Fucks_A_Lil_Elf's_Big_Cock_/Julia-Ann-Naughty-America-%E6%80%A7%E6%84%9F%E7%9A%84%E8%A3%98%E8%8E%89%E5%A9%AD%E5%AE%89%E4%BA%82%E6%90%9E-lil-%E7%B2%BE%E9%9D%88%E7%9A%84%E5%A4%A7%E5%85%AC%E9%9B%9E-2020

www_oracle_com的博客 22万+

指令重排序和半初始化问题

‌指令重排序(Instruction Reordering)‌ 是指编译器和处理器在不改变程序‌单线程执行结果‌的前提下,对代码的指令执行顺序进行重新排列,以提高运行效率的优化手段

xiaowangbadan0_0的博客 1029

YOLOv12改进策略【独家融合改进】| SPD-Conv+PPA 再次提升模型针对小目标的特征提取能力

SPD-Conv是一种新的 CNN 构建模块,用于替代传统 CNN 架构中使用的步长卷积(strided convolution)和池化(pooling)层,它由空间到深度(Space-to-depth,SPD)层和非步长卷积(non - strided convolution)层组成。

Limiiiing的博客 1028

基于深度学习的野生菌菇检测识别分类系统(YOLOv8 + UI界面 + 数据集)

随着深度学习技术的迅猛发展,计算机视觉领域的应用得到了广泛的关注。野生菌菇的检测与识别是一个复杂而又有趣的问题,涉及到物体检测、图像分类以及自然语言处理等技术。本文将介绍如何基于YOLOv8模型,结合自定义UI界面与数据集,构建一个高效且精确的野生菌菇检测识别分类系统。首先,我们将介绍YOLOv8的原理与特点,然后详细描述如何构建和训练野生菌菇识别模型,并介绍如何为该系统设计UI界面,最终形成一个完整的系统。本文内容包括代码实现、模型训练及数据集准备等,旨在为有类似需求的开发者提供参考和帮助。目录摘要目录。

YOLO 1547

java并发之重排序问题

在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。重排序分3种类型。1编译器优。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。2级。现代处理器采用了指令级并行技术ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。3统。由于处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

weixin_69039908的博客 820

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

多线程重排序问题

为什么要重排序? 根据程序顺序规则:在不改变程序运行结果前提下,尽可能提高并行度.也就是为了提高性能改变代码的执行顺序. 什么样的代码不会重排序? 数据依赖的数据,比如后面的代码执行需要用到前面的代码, 简单说两个操作其中如果有一个操作是读操作者两个操作具有数据依赖性 存在数据依赖性的操作不会重排序 重排序遵守什么规则? as-if-serial: 不管怎么排序,结果不能改变 不存在数据依赖的可...

李勇杰 800

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

指令重排序及可见性问题

指令重排序及可见性问题指令重排序概念重排序的情况可见性问题CPU层面的问题具体实现:内存屏障x86架构的内存屏障编译器层面的问题 小渣渣,如有问题,欢迎指正。 指令重排序 概念 指令重排序,顾名思义,就是对指令的执行顺序重新进行排序。 举个例子 test{ A(); B(); C(); …… } 加入一个这样的方法,执行过程为A(),B(),C(),而且这3个指令互不影响,相互隔离。 ...

一只jackey博客 1665

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

指令重排序导致的可见性问题

01 什么是指令重排序 指令重排序是指编译器或CPU为了优化程序的执行性能而对指令进行重新排序的一种手段,重排序会带来可见性问题,所以在多线程开发中必须要关注并规避重排序。 从源代码到最终运行的指令,会经过如下两个阶段的重排序。 第一阶段,编译器重排序,就是在编译过程中,编译器根据上下文分析对指令进行重排序,目的是减少CPU和内存的交互,重排序之后尽可能保证CPU从寄存器或缓存行中读取数据。 在前面分析JIT优化中提到的循环表达式外提(Loop Expression Hoisting)就是编译器层面的重排序

博文视点(北京)官方博客 2255

Java并发编程二:重排序问题

重点知识来了,建议收藏哦 1. 重排序概念 在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。 从 java 源代码到最终实际执行的指令序列,会分别经历下面三种重排序: 编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。 指令级并行的重排序。处理器将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。 内存系统的重排序。处理器使用缓存和读/写缓冲区,使得加载和存储操作看上去可能是在乱序执行。 举例:如下代码执行过程中,程序不一

qq_43623970的博客 459

什么是重排序问题

十七、 什么是重排序问题Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序可以保证最终执行的结果是与程序顺序执行的结果一致,并且只会对不存在数据依赖性的指令进行重排序,这个重排序在单线程下对最终执行结果是没有影响的,但是在多线程下就会存在问题。一个例子int a = 1;(1) int b = 2;(2) int c= a + b;(3) 如上c的值依赖a和b的值,所以重排序后能够

加多的博客 1521

什么是重排序?重排序会产生什么问题?什么是happenbefore?应用happen-before的具体实例

什么是重排序? 执行任务的时候,为了提高编译器和处理器的执行性能,编译器和处理器(包括内存系统,内存在行为没有重排但是存储的时候是有变化的)会对指令重排序。编译器优化的重排序是在编译时期完成的,指令重排序和内存重排序是处理器重排序 编译器优化的重排序,在不改变单线程语义的情况下重新安排语句的执行顺序 指指令级并行重排序,处理器的指令级并行技术将多条指令重叠执行,如果不存在数据的依赖性将会改变语句...

qq_41174684的博客 5207

Java中volatile关键字如何解决指令重排序导致的可见性问题?

volatile通过​​内存屏障​​​禁止指令重排序​​:确保操作顺序符合程序预期。​​强制内存可见性​​:保证写操作立即可见,读操作获取最新值。在状态标志、安全发布等场景下,它是高效解决可见性与有序性问题的轻量级方案,但需注意其​​不适用于复合原子操作​​的场景。

iddlw75046的博客 600

volatile和synchronized的原子性以及重排序造成的问题

单例模式中分为懒汉和饥汉. 双重加锁是针对懒汉而言的,就是能不加载就不加载,只有当需要的时候才会加载,也可称之为 延迟加载. 但是双重加载会有一些问题:虽然synchronized可以保证同一时间只有一个线程操作代码块,但是当创建单例对象的时候会出现重排序问题. 具体原因可以参见这两篇: https://www.cnblogs.com/a154627/p/10046147.html https:...

qq_38409944的博客 867

排序问题java_Java并发编程二:重排序问题

重点知识来了,建议收藏哦1. 重排序概念在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。从 java 源代码到最终实际执行的指令序列,会分别经历下面三种重排序:img编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。指令级并行的重排序。处理器将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。内存系统的重排序。处理器...

weixin_32787485的博客 275

12.重排序带来的可见性问题

对于编译器,JMM的编译器重排序规则会禁止特定类型的编译器重排 序(不是所有的编译器重排序都要禁止)。对于处理器重排序,JMM的处理器重排序规则会要 求Java编译器在生成指令序列时,插入特定类型的内存屏障(Memory Barriers,Intel称之为 Memory Fence)指令,通过内存屏障指令来禁止特定类型的处理器重排序。JMM属于语言级的内存模型,它确保在不同的编译器和不同的处理器平台之上,通过禁止特定类型的编译器重排序和处理器重排序,为程序员提供一致的内存可见性保证。3)内存系统的重排序。.

纵横千里 捭阖四方 的专栏 310

Java内存模型中的线程重排序问题及解决方法

线程重排序是多线程编程中一个常见的问题,可能导致程序行为与预期不符。通过使用块,我们可以有效地解决重排序问题,确保线程之间的操作顺序符合预期。在实际开发中,我们需要时刻警惕重排序问题,并合理使用同步机制来保证程序的正确性。

m0_74337424的博客 491

UICollectionView 重排序项目常见问题解决方案

UICollectionView 重排序项目常见问题解决方案 项目基础介绍 uicollectionview-reordering 是一个开源项目,旨在为 UICollectionView 提供简单易用的重排序功能。该项目主要使用 Swift 编程语言开发,适用于 iOS 应用程序开发。通过该项目,开发者可以轻松实现 UICollectionView 中的单元格重排序功能,提升用户体验。 新手使用...

gitblog_01008的博客 359
上一篇: Volatile关键字
下一篇: 使用并发队列实现生产者与消费者
Art&Code
博客等级 码龄7年 77粉丝 554原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值