指令重排序

在多线程并发编程的过程中,执行重排序有时候会造成错误的后果,比如一个线程在main线程中调用setFlag(true)的前边修改了某些程序配置项,而在t1线程里需要用到这些配置项,所以会造成配置缺失的错误。但是java给我们提供了一些抑制指令重排序的方式。

1.同步代码抑制指令重排序

将需要抑制指令重排序的代码放入同步代码块中:

在获取锁的时候,它前边的操作必须已经执行完成,不能和同步代码块重排序;在释放锁的时候,同步代码块中的代码必须全部执行完成,不能和同步代码块后边的代码重排序。

2.volatile变量抑制指令重排序

public class Reordering {

    private static volatile boolean flag;
    private static int num;

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                while (!flag) {
                    Thread.yield();
                }

                System.out.println(num);
            }
        });
        t1.start();
        num = 5;
        flag = true;
    }
}

  

具体抑制重排序的规则如下:

  1. volatile写之前的操作不会被重排序到volatile写之后。
  2. volatile读之后的操作不会被重排序到volatile读之前。
  3. 前边是volatile写,后边是volatile读,这两个操作不能重排序。

3.final变量抑制指令重排序

具体的规则就这两条:

  1. 在构造方法内对一个final字段的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
  2. 初次读一个包含final字段对象的引用,与随后初次读这个final字段,这两个操作不能重排序。

原文地址:https://www.cnblogs.com/hongchengshise/p/10409941.html

时间: 2024-11-08 20:54:00

指令重排序的相关文章

Java的多线程机制系列:不得不提的volatile及指令重排序(happen-before)

一.不得不提的volatile volatile是个很老的关键字,几乎伴随着JDK的诞生而诞生,我们都知道这个关键字,但又不太清楚什么时候会使用它:我们在JDK及开源框架中随处可见这个关键字,但并发专家又往往建议我们远离它.比如Thread这个很基础的类,其中很重要的线程状态字段,就是用volatile来修饰,见代码 /* Java thread status for tools, * initialized to indicate thread 'not yet started' */   p

J.U.C JMM. pipeline.指令重排序,happen-before(续)

前面已经介绍硬件平台Cache Coherence问题和解决办法,下面来看看Java虚拟机平台的相关知识.硬件平台处理器,高速缓存,主存之间的交互关系如下: Java内存模型(JMM)        Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model, JMM)来屏蔽掉底层各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果. 主内存和工作内存       Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟

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

在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到锁机制,因此此小节中会适当引入锁的概念. 在Java Concurrency in Practice中是这样定义线程安全的: 当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替运行,并且不需要额外的同步及在调用方代码不必做其他的协调,这个类的行为仍然是正确的,那么这个类就是线程安全的. 显然只有资源竞争时才会导致线程不安全,因此无状态对象永远是线程安全的. 原子操作的描述是: 多个线程执行一个操作时,其

理解并发编程中的重要概念:指令重排序和指令乱序执行

看过了很多介绍指令重排序的文章,可惜由于自己硬件和计算机理论知识缺乏,很难理解深层次的奥秘和实现原理.不过也有很多帖子,讲的浅显易懂,使用的例子很形象.大牛就是能用简单的解释和通俗的比喻,给我们讲明白很高深的东西.这里做个摘抄和总结,和大家分享下,希望大家能够对指令重排序有个形象的认识,不至于在并发编程中犯一些简单的错误.如果理解有错误,希望看到的大神指正. 从源码变成可以被机器(或虚拟机)识别的程序,至少要经过编译期和运行期.重排序分为两类:编译期重排序和运行期重排序(乱序执行),分别对应编译

多线程之指令重排序

为什么会乱序 现在的CPU一般采用流水线来执行指令.一个指令的执行被分成:取指.译码.访存.执行.写回.等若干个阶段.然后,多条指令可以同时存在于流水线中,同时被执行. 指令流水线并不是串行的,并不会因为一个耗时很长的指令在"执行"阶段呆很长时间,而导致后续的指令都卡在"执行"之前的阶段上. 相反,流水线是并行的,多个指令可以同时处于同一个阶段,只要CPU内部相应的处理部件未被占满即可.比如说CPU有一个加法器和一个除法器,那么一条加法指令和一条除法指令就可能同时处

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

[转]http://my.oschina.net/chihz/blog/58035 文章写的非常好,为作者点赞. JAVA内存模型 对于我们平时开发的业务应用来说,内存应该是访问速度最快的存储设备,对于频繁访问的数据,我们总是习惯把它们放到内存缓存中,有句话不是说么,缓存就像是清凉油,哪里有问题就抹一抹.但是CPU的运算速度比起内存的访问速度还要快几个量级,为了平衡这个差距,于是就专门为CPU引入了高速缓存,频繁使用的数据放到高速缓存当中,CPU在使用这些数据进行运算的时候就不必再去访问内存.但

指令重排序 as-if-serial

笔者认为看完一本书或刚要了解完一个知识点  最好自己先运行一些DEMO 自己尝试着去了解下各种意思  这样知识点最终一定是你的.靠死记硬背的讨论或简单的粗暴的看下资料 脑子里肯定还是一团浆糊. p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco } p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; min-height: 15.0px } p.p3 { ma

关于volatile的可见性和禁止指令重排序的疑惑

在学习volatile语义的可见性和禁止指令重排序的相关测试中,发现并不能体现出禁止指令重排序的特性 实验代码如下 package com.aaron.beginner.multithread.volatiletest; import java.util.concurrent.CountDownLatch; /** * @author * @description 一句话描述该文件的用途 * @date 2017-03-01 */ public class VolatileAndNonVolat

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

转: http://www.blogjava.net/xylz/archive/2010/07/03/325168.html 在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到锁机制,因此此小节中会适当引入锁的概念. 在Java Concurrency in Practice中是这样定义线程安全的: 当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替运行,并且不需要额外的同步及在调用方代码不必做其他的协调,这个类的行为仍然是正确的,那么这个类就是线程安

java指令重排序的问题

转载自于:http://my.oschina.net/004/blog/222069?fromerr=ER2mp62C 指令重排序是个比较复杂.觉得有些不可思议的问题,同样是先以例子开头(建议大家跑下例子,这是实实在在可以重现的,重排序的概率还是挺高的),有个感性的认识 /** * 一个简单的展示Happen-Before的例子. * 这里有两个共享变量:a和flag,初始值分别为0和false.在ThreadA中先给 a=1,然后flag=true. * 如果按照有序的话,那么在ThreadB