源代码和Runtime时执行的代码很可能不一样,这是因为编译器、处理器常常会为了追求性能对改变执行顺序。然而改变顺序执行很危险,很有可能使得运行结果和预想的不一样,特别是当重排序共享变量时。

 从源代码到Runtime需要经过三步的重排序:

编译器重排序

 为了提高性能,在不改变单线程的执行结果下,可以改变语句执行顺序。

 比如尽可能的减少寄存器的读写次数,充分利用局部性。像下面这段代码这样,交替的读x、y,会导致寄存器频繁的交替存储x和y,最糟的情况下寄存器要存储3次x和3次y。如果能让x的一系列操作一块做完,y的一块做完,理想情况下寄存器只需要存储1次x和1次y。

//优化前
int x = 1;
int y = 2;
int a1 = x * 1;
int b1 = y * 1;
int a2 = x * 2;
int b2 = y * 2;
int a3 = x * 3;
int b3 = y * 3; //优化后
int x = 1;
int y = 2;
int a1 = x * 1;
int a2 = x * 2;
int a3 = x * 3;
int b1 = y * 1;
int b2 = y * 2;
int b3 = y * 3;

指令重排序

 指令重排序是处理器层面做的优化。处理器在执行时往往会因为一些限制而等待,如访存的地址不在cache中发生miss,这时就需要到内存甚至外存去取,然而内存和外区的读取速度比CPU执行速度慢得多。

 早期处理器是顺序执行(in-order execution)的,在内存、外存读取数据这段时间,处理器就一直处于等待状态。现在处理器一般都是乱序执行(out-of-order execution),处理器会在等待数据的时候去执行其他已准备好的操作,不会让处理器一直等待。

 满足乱序执行的条件:

  1. 该缓存的操作数缓存好
  2. 有空闲的执行单元

 对于下面这段汇编代码,操作1如果发生cache miss,则需要等待读取内存外存。看看有没有能优先执行的指令,操作2依赖于操作1,不能被优先执行,操作3不依赖1和2,所以能优先执行操作3。

所以实际执行顺序是3>1>2

LDR R1, [R0];//操作1
ADD R2, R1, R1;//操作2
ADD R3, R4, R4;//操作3

内存系统重排序

 由于处理器有读、写缓存区,写缓存区没有及时刷新到内存,造成其他处理器读到的值不是最新的,使得处理器执行的读写操作与内存上反应出的顺序不一致

 如下面这个例子,可能造成处理器A读到的b=0,处理器B读到的a=0。A1写a=1先写到处理器A的写缓存区中,此时内存中a=0。如果这时处理器B从内存中读a,读到的将是0。

 以处理器A来说,处理器A执行的顺序是A1>A2>A3,但是由于写缓存区没有及时刷新到内存,所以实际顺序为A2>A1>A3。

初始化:
a = 0;
b = 0; 处理器A执行
a = 1; //A1
read(b); //A2 处理器B执行
b = 2; //B1
read(a); //B2

阻止重排序

 不论哪种重排序都可能造成共享变量中线程间不可见,这会改变程序运行结果。所以需要禁止对那些要求可见的共享变量重排序。

  • 阻止编译重排序:禁止编译器在某些时候重排序。
  • 阻止指令重排序和内存系统重排序:使用内存屏障或Lock前缀指令

从源代码到Runtime发生的重排序的更多相关文章

  1. Java并发编程原理与实战四十一:重排序 和 happens-before

    一.概念理解 首先我们先来了解一下什么是重排序:重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段. 从Java源代码到最终实际执行的指令序列,会分别经历下面3种重排序,如下图 ...

  2. 指令重排序及Happens-before法则随笔

    指令重排序 对主存的一次访问一般花费硬件的数百次时钟周期.处理器通过缓存(caching)能够从数量级上降低内存延迟的成本这些缓存为了性能重新排列待定内存操作的顺序.也就是说,程序的读写操作不一定会按 ...

  3. 多线程学习:Volatile与Synchronized的区别、什么是重排序

    java线程的内存模型 java的线程内存模型中定义了每个线程都有一份自己的共享变量副本(本地内存),里面存放自己私有的数据,其他线程不能直接访问,而一些共享变量则存在主内存中,供所有线程访问. 上图 ...

  4. 深入浅出Java并发包—指令重排序

    前面大致提到了JDK中的一些个原子类,也提到原子类是并发的基础,更提到所谓的线程安全,其实这些类或者并发包中的这么一些类,都是为了保证系统在运行时是线程安全的,那到底怎么样才算是线程安全呢? Java ...

  5. JVM并发机制的探讨——内存模型、内存可见性和指令重排序

    并发本来就是个有意思的问题,尤其是现在又流行这么一句话:“高帅富加机器,穷矮搓搞优化”. 从这句话可以看到,无论是高帅富还是穷矮搓都需要深入理解并发编程,高帅富加多了机器,需要协调多台机器或者多个CP ...

  6. 深入浅出 Java Concurrency (4): 原子操作 part 3 指令重排序与happens-before法则

    转: http://www.blogjava.net/xylz/archive/2010/07/03/325168.html 在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到 ...

  7. Jvm 中的 重排序、主存、原子操作

    一.重排序 好处:重排序可以提升性能,避免在一个耗时很长的指令在“执行”阶段呆很长时间,而导致后续的指令都卡在“执行”之前的阶段上. 坏处:重排序对多线程的影响 class ReorderExampl ...

  8. 深入浅出 Java Concurrency (4): 原子操作 part 3 指令重排序与happens-before法则[转]

    在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到锁机制,因此此小节中会适当引入锁的概念. 在Java Concurrency in Practice中是这样定义线程安全的: ...

  9. JMM中的重排序及内存屏障

    目录 1. 概述 2. 重排序 2-1. as-if-serial语义 2-2. 重排序的种类 2-3. 从Java源代码到最终实际执行的指令序列, 会分别经历下面3中重排序. 3. 内存屏障类型 3 ...

随机推荐

  1. web前端免费资源集

    web前端免费资源集 https://github.com/vhf/free-programming-books/blob/master/free-programming-books-zh.md

  2. shapes 不规则边界

    CSS基本形状都是矩形,CSS shapes允许开发者用定制的路径来包裹内容,例如圆形,椭圆,多边形等.   形状可以自定义也可以从图片中推断   从图片推断,例如可以让文字按照图片形状来浮动到一边: ...

  3. Ajax 跨域 异步 CORS

    HTTP access control (CORS) 核心在于使用定制(添加新的header)HTTP header让浏览器和服务器有更多的相互了解,从而决定一个请求或者响应成功还是失败   对于一个 ...

  4. Linux 新建文件/文件夹,删除文件文件夹,查找文件 打开文件

    1.新建文件夹:mkdir xx 2.新建文件: touch 1.py 3.删除文件/文件夹: rm -r xx  rm 1.py 4.打开文件:cat 1.py 只显示前几行 :head -2 1. ...

  5. Java_String_01_由转义字符串得到其原本字符串

    在开发企业微信电子发票之拉取电子发票接口的时候,微信服务器会发送给我们一个2层的转义字符串,而我们要想得到我们想要的结果,就需要进行一些处理: 反转义+去除首尾双引号. 一.需求 现有一个字符串 st ...

  6. [J2EE] 有关 PreparedStatement

    今天同事遇到一个问题,简言之,就是PreparedStatement的预编译究竟是怎么发挥作用的... 嘿嘿,说来惭愧,我以前就只知道PreparedStatement比Statement要好,要防S ...

  7. Oracle的Recyclebin策略

    1.从oracle10g开始删除数据库表的时候并不是真正删除,而是放到了recyclebin中,这个过程类似 windows里面删除的文件会被临时放到回收站中. 2.删除的表系统会自动给他重命名就是你 ...

  8. phpcms 的getcache()函数

    一直没有去研究phpcms 的getcache()函数是干嘛的,今天有空去看了一下,原来就那样. 1 function getcache($name, $filepath='', $type='fil ...

  9. 你不得不看的Python机器学习工具

    IEEE Spectrum排行榜第一,Skill UP排名第一的开发工具,Stack Overflow年度调查中程序员最感兴趣的选择,Stack Overflow 6月份访问量最多的编程语言..... ...

  10. SQLSERVER实现更改表名,更改列名,更改约束代码

    1.修改表名 格式:sp_rename tablename,newtablename ? 1 sp_rename tablename,newtablename 2.修改字段名 格式:sp_rename ...