深入理解Java内存模型(二)——重排序

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

(原文地址:http://www.infoq.com/cn/articles/java-memory-model-2)
数据依赖性

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

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

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

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

as-if-serial语义

as-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同样遵从这一目标。

重排序对多线程的影响

现在让我们来看看,重排序是否会改变多线程程序的执行结果。请看下面的示例代码:

class ReorderExample {
int a = 0;
boolean flag = false;

public void writer() {
    a = 1;                   //1
    flag = true;             //2
}

Public void reader() {
    if (flag) {                //3
        int i =  a * a;        //4
        ……
    }
}
}

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语义允许对存在控制依赖的操作做重排序的原因);但在多线程程序中,对存在控制依赖的操作重排序,可能会改变程序的执行结果。

Code Ocean 上传代码 手把手教程 对于一些学术期刊,录用前会让把代码上传到Code Ocean上,但上传过程可能会遇到一些问题,小编分享一下自己的上传经验。他们会进行验证,验证通过,将自动发布。点此运行即可,但是运行比较慢。 阅读详情

相关推荐

adams案例分析_案例1(麦弗逊悬架子系统创建)

本文字数3292字83图,建议阅读时间26分钟首先看下案例1的麦弗逊悬架数模结构就不细讲了,从数模上主要是获取硬点坐标(铰接点、安装点、参考点)及大部件结构(副车架、摆臂、转向节等实际运动过程中会产生变形的部件,在ADAMS Car中我们就可以用柔性体来搭建)再来回顾下上一篇讲到的模型元素1、Hardpoint——硬点(只有位置没有方向)2、Parameter Variable——参数...

weixin_39609407的博客 6479

指令重排序

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

Waiting_Mr_Liu的博客 5936

随机化策略——随机变量rand、约束constraint、权重dist、随机数产生示例

文章目录一、随机变量类型1.1.rand随机变量1.2.randc随机变量二、随机化约束2.1.constraint指定约束范围2.1.1.关系操作符约束(>、<、=、>=、<=)2.1.2.范围操作符约束(inside)2.1.3.权重分布约束(dist)2.1.4.条件约束(if-else; ->)三、启动随机化——randomize( )函数四、关闭或打开随机变...

Mr.翟的博客 2万+

Java并发编程之指令重排序

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

weixin_44692700的博客 4856

TSmaster CANFD配置避坑指南:为什么你的PCAN盒不支持最新版本?

本文针对TSmaster配置PCAN硬件进行CANFD通信时的常见问题,提供了详细的避坑指南。重点解析了CANFD配置中TSEG1等关键参数的理解误区,并深入探讨了TSmaster新版本与PCAN硬件驱动之间的兼容性变化,帮助工程师快速定位并解决连接失败或无法发送CANFD报文等问题。

alpha的博客 545

全志DRM框架下LVDS双屏异显配置实战

本文详细介绍了在全志DRM框架下配置LVDS双屏异显的实战经验。涵盖硬件连接、内核配置、设备树修改、背光控制及U-Boot适配等关键步骤,重点解析屏参转换技巧与双屏route配置,帮助开发者解决工业控制台等多屏应用中的显示适配问题,提升开发效率。

rnn9storyteller的博客 814

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

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

NowOrNever 7936

大数据与宠物:健康监测数据分析

随着全球宠物市场规模的快速增长(2023年全球宠物产业规模已达3500亿美元),宠物健康管理需求呈现指数级上升。传统宠物医疗依赖定期体检和症状观察,存在滞后性和主观性缺陷。本文聚焦如何通过物联网(IoT)设备采集宠物生理数据(体温、心率、活动量等),利用大数据分析技术构建实时健康监测系统,实现宠物健康状态的动态评估、异常预警和疾病预测。研究范围涵盖数据采集技术、数据预处理方法、机器学习模型构建及实际应用场景落地。背景介绍:明确研究目的、目标读者及核心术语核心概念与联系。

2502_91592937的博客 1068

求帮忙!!!!急急急

小编码大脑门RU9CRC43aWdxNDs3NDFSOzFpa1I1MWliT08w获取Flag

weixin_42213572的博客 6474

【AI智能体】Codex 高级进阶使用技巧项目实战操作详解

Codex 高级进阶使用技巧项目实战操作详解

congge 1万+

2022中山大学计算机技术专硕考研初试、复试经验帖

个人简介 推荐几个我自己感觉对考研非常有帮助的小助手吧。可以帮助节省时间! 考研时间规划总览 初试篇 数学 英语 政治 408 复试篇 机试 笔试 面试 关于选导师 个人感言

zik的博客 1万+

c++STL急急急

c++STL急急急

qq_51931826的博客 3904

筑基篇第9节:CUDA内存管理API进阶

CUDA多维内存管理与零拷贝技术 本文介绍了CUDA中高效处理多维数据的内存管理技术。对于二维数据,使用cudaMallocPitch可确保内存对齐访问,提升75%的带宽利用率;三维数据则通过cudaMalloc3D实现切片对齐。配套的cudaMemcpy2D/3D函数能高效处理数据拷贝。此外,零拷贝内存技术允许内核直接访问主机内存,避免显存拷贝开销。这些技术对图像处理、科学计算等应用至关重要,能显著提升多维数据处理的性能。

PeterPan的博客 83

急急急!!!本人新手,急求大佬帮忙看一下这是什么问题?

用Android studio 2.1写的 如题,简单的调用一个Media Player来播放一首歌曲,语法应该没啥错误,但是在模拟机上运行的时候,程序可以打开,但是按播放按钮的时候就这样了。(歌曲也放在路径下了) 代码如下,大佬们帮看看到时是代码的问题,还是缺什么东西没装? package com.example.dy.musicplayer; import android.media...

aafafas的博客 266

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

matlab中trel,求助求助!!!!急急急!!!

该楼层疑似违规已被系统折叠隐藏此楼查看此楼clear;clc;SNR=10; % 信噪比fl=128; % 设置FFT长度Ns=6; %设置一个祯结构中OFDM信号的个数para=128;%设置并行传输的子载波个数sr=250000; %符号速率br=sr.*2;% 每个子载波的比特率gl=32; %保护时隙的长度SNRdB=0:0.5:3;%信噪比范围declen=40;%译码深度SN...

weixin_28956075的博客 441

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Unterminated <%@ page tag 急急急!!!!

 这些是我做的order.jsp程序段!session.removeAttribute("cxtj");%> var DS_x,DS_y; function dateSelector() //构造dateSelector对象,用来实现一个日历形式的日期输入框。 { var myDate=new Date(); this.year=myDate.getFullYear()+1; //定义

ljn312的专栏 9202

什么是Java中的指令重排序?

指令重排序(Instruction Reordering)是现代计算机系统中优化性能的一种手段,通过改变语句的执行顺序来提高指令的并行度,从而提高执行效率。在Java中,指令重排序主要体现在编译器优化重排、指令并行重排和内存系统重排三个方面。

qq_33326733的博客 2764

volatile指令重排

计算机在执行程序时,为了提高性能,编译器和处理器常常会对指令重排,一般分为以下三种: 源代码 -> 编译器优化的重排 -> 指令并行的重排 -> 内存系统的重排 -> 最终执行指令 单线程环境里面确保最终执行结果和代码顺序的结果一致 处理器在进行重排序时,必须要考虑指令之间的数据依赖性 多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。 指令重排 - example 1# public void

没有伞的孩子必须努力奔跑!—小林 2441

volatile 指令重排以及为什么禁止指令重排

这里是引用 java指令重排以及volatile 为什么禁止指令重排指令重排序什么是指令重排序为什么要进行指令重排序如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 指令重排序 什么是指令重排序 处理器对写入的代码进行乱序执行优化, 并保证 优化后的执行 结果和正常执.

weixin_43982927的博客 5273

Volatile指令重排详解

一、Volatile不可见性 Volatile关键 二、CPU的乱序执行 CPU在进行读等待的同时执行指令,是CPU乱序的根源,不是乱,而是提高效率 三、对象的创建过程 对象的创建过程: 创建->初始化->建立连接 1.先申请内存,赋值默认值0 2.构造方法赋值初始值,8 3.建立连接,t->T 四、DCL单例 4.1 DCL单例代码 DCL单例模式 public class Singleton { pr...

Jak的博客 6100

Java内存模型之重排序

1.什么是重排序 2.重排序的好处 3.重排序的三种情况 4.用volatile修正重排序问题

脚踏实地,仰望星空 1360

指令重排序(happens-before + As-If-Serial原则)

转自: https://segmentfault.com/a/1190000011458941 https://blog.youkuaiyun.com/qq_19642249/article/details/81002210 Java多线程可见性 在现代操作系统上编写并发程序时,除了要注意线程安全性(多个线程互斥访问临界资源)以外,还要注意多线程对共享变量的可见性,而后者往往容易被人忽略。 可见性是指当一个线...

weixin_43751710的博客 511

简单谈谈什么是指令重排序?

实例通俗告诉你什么是指令重排序

m0_51358164的博客 1258
上一篇: 深入理解Java内存模型(一)——基础
下一篇: 深入理解Java内存模型(三)——顺序一致性
三易的博客
博客等级 码龄15年 42粉丝 89原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值