精华内容
参与话题
问答
  • 指令重排序

    千次阅读 2016-02-15 20:36:00
    指令重排序 重排序的目的是提高运行并发度,发生在编译器和处理器阶段,遵循as-if-serial语义(不管怎么重排序,单线程程序的执行结果不能改变),也就是重排序所带来的问题是针对多线程的。 重排序发生的条件是A...
    指令重排序
    重排序的目的是提高运行并发度,发生在编译器和处理器阶段,遵循as-if-serial语义(不管怎么重排序,单线程程序的执行结果不能改变),也就是重排序所带来的问题是针对多线程的。
    重排序发生的条件是A和B没有存在依赖关系,这里的依赖关系是指数据依赖关系和控制依赖关系两种。其中数据依赖表示两个以上操作访问同一个变量,且这两个操作中有一个为写操作。而控制依赖关系,比如if(a>0){int i = a*a;}。

    很多介绍JVM并发的书或文章都会谈到JVM为了优化性能,采用了指令重排序,但是对于什么是指令重排序,为什么重排序会优化性能却很少有提及,其实道理很简单,假设有这么两个共享变量a和b:

    private int a;
    private int b;

    在线程A中有两条语句对这两个共享变量进行赋值操作:
    a = 1;
    b = 2;

    假设当线程A对a进行复制操作的时候发现这个变量在主内存已经被其它的线程加了访问锁,那么此时线程A怎么办?等待释放锁?不,等待太浪费时间了,它会去尝试进行b的赋值操作,b这时候没被人占用,因此就会先为b赋值,再去为a赋值,那么执行的顺序就变成了:
    b = 2;
    a = 1;

    对于在同一个线程内,这样的改变是不会对逻辑产生影响的,但是在多线程的情况下指令重排序会带来问题,看下面这个情景:
    在线程A中:
    context = loadContext();
    inited = true;
    在线程B中:
    while(!inited ){
        sleep
    }
    doSomethingwithconfig(context);

    假设A中发生了重排序:
    inited = true;
    context = loadContext();

    那么B中很可能就会拿到一个尚未初始化或尚未初始化完成的context,从而引发程序错误。

    想到有一条古老的原则很适合用在这个地方,那就是先要保证程序的正确然后再去优化性能。此处由于重排序产生的错误显然要比重排序带来的性能优化要重要的多。要解决重排序问题还是通过volatile关键字,volatile关键字能确保变量在线程中的操作不会被重排序而是按照代码中规定的顺序进行访问。

    展开全文
  • 这段话我有疑问:指令重排序会破坏happens-before原则吗?happens-before的程序次序原则说:在一个线程内,按照程序代码顺序,书写在前面的操作会先行发生于书写在后面的操作。 如果线程内出现指令重...
  • 最近在看《Java多线程编程实战指南》,在第P110页中是这么说的: ...所以小弟想问到底volatile的读/写操作能否与该操作之前/后的指令重排序?如果不能那么JVM中的内存屏障是如何设置的呢? 若能回答不胜感激!
  • 下面小编就为大家带来一篇浅谈java指令重排序的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 禁止指令重排序

    千次阅读 2018-04-28 12:36:02
    volatile的有序性是使用内存屏障实现的,它能禁止指令重排序。 在执行程序时为了提高性能,编译器和处理器通常会对指令做重排序: 编译器重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行...

    volatile的有序性是使用内存屏障实现的,它能禁止指令重排序

    在执行程序时为了提高性能,编译器和处理器通常会对指令做重排序:

    1. 编译器重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序;
    2. 处理器重排序。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序;

    指令重排序对单线程没有什么影响,他不会影响程序的运行结果,但是会影响多线程的正确性,那么我们就需要禁止重排序。

    如果两个操作的执行顺序无法从happens-before原则中推到出来,那么他们就不能保证有序性,可以随意进行重排序。

    1. 同一个线程中的,前面的操作 happen-before 后续的操作。(即单线程内按代码顺序执行。但是,在不影响在单线程环境执行结果的前提下,编译器和处理器可以进行重排序,这是合法的。换句话说,这一是规则无法保证编译重排和指令重排)。
    2. 监视器上的解锁操作 happen-before 其后续的加锁操作。(Synchronized 规则)
    3. 对volatile变量的写操作 happen-before 后续的读操作。(volatile 规则)
    4. 线程的start() 方法 happen-before 该线程所有的后续操作。(线程启动规则)
    5. 线程所有的操作 happen-before 其他线程在该线程上调用 join 返回成功后的操作。
    6. 如果 a happen-before b,b happen-before c,则a happen-before c(传递性)。

    分析:

    • 程序次序规则:一段代码在单线程中执行的结果是有序的。注意是执行结果,因为虚拟机、处理器会对指令进行重排序(重排序后面会详细介绍)。虽然重排序了,但是并不会影响程序的执行结果,所以程序最终执行的结果与顺序执行的结果是一致的。故而这个规则只对单线程有效,在多线程环境下无法保证正确性。
    • 锁定规则:这个规则比较好理解,无论是在单线程环境还是多线程环境,一个锁处于被锁定状态,那么必须先执行unlock操作后面才能进行lock操作。
    • volatile变量规则:这是一条比较重要的规则,它标志着volatile保证了线程可见性。通俗点讲就是如果一个线程先去写一个volatile变量,然后一个线程去读这个变量,那么这个写操作一定是happens-before读操作的。
    • 传递规则:提现了happens-before原则具有传递性,即Ahappens-before B , B happens-before C,那么Ahappens-before C
    • 线程启动规则:假定线程A在执行过程中,通过执行ThreadB.start()来启动线程B,那么线程A对共享变量的修改在接下来线程B开始执行后确保对线程B可见。
    • 线程终结规则:假定线程A在执行的过程中,通过制定ThreadB.join()等待线程B终止,那么线程B在终止之前对共享变量的修改在线程A等待返回后可见。

    Volatile的使用场景:

    • 对变量的写操作不依赖当前值;
    • 该变量没有包含在具有其他变量的不变式中。
    展开全文
  • Java 指令重排序

    2019-07-24 10:41:36
    Java 指令重排序 Java 内存模型运行编译器和处理器对指令重排序以提高运行性能,并且只会对不存在数据依赖性的指令重排序。在单线程下可以保证最终的结果与程序顺序执行的结果一致,但是在多线程下就会存在问题。 ...

    Java 指令重排序

    Java 内存模型运行编译器和处理器对指令重排序以提高运行性能,并且只会对不存在数据依赖性的指令重排序。在单线程下可以保证最终的结果与程序顺序执行的结果一致,但是在多线程下就会存在问题。

    例如:
    int a = 1;(1)
    int b = 2;(2)
    int c = a + b;(3)
    

    在上面代码中,变量 c 的值依赖 a 和 b 的值,所以重排序后能够保证 (3) 的操作在 (2)和(1)之后,但是(1)(2)谁先执行就不一定了,这在单线程下不会存在问题,因为并不影响最终的结果。

    Public class Test {
    
        private static int num = 0;
        private static boolean ready = false;
    
        public static class ReadThread extends Thread {
    
            @Override
            public void run() {
    
                while (!Thread.currentThread().isInterrupted()) {
                    if (ready) {//(1)
                        System.out.println(num + num);//(2)
                    }
                    System.out.println("read thread ...");
                }
            }
        }
    
        public static class WriteRead extends Thread {
    
            @Override
            public void run() {
                num = 2;//(3)
                ready = true;//(4)
                System.out.println("WriteRead set over");
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            ReadThread rt = new ReadThread();
            rt.start();
    
            WriteRead wr = new WriteRead();
            wr.start();
    
            Thread.sleep(10);
            rt.interrupt();
            System.out.println("main exit");
        }
    }
    

    这段代码里面的变量没有被声明为 volatile的,也没有使用任何同步措施,所以在多线程下存在共享变量内存可见性问题。这里先不谈可见性问题,因为通过把变量声明为 volatile 的本身就可以避免指令重排序问题。

    上面代码在不考虑内存可见性问题的情况下不一定会输出4。由于代码(1)(2)(3)(4)之间不存在依赖关系,所以写线程的代码(3)(4)可能被重排序为先执行(4)再执行(3),那么执行(4)后,读线程可能已经执行了(1)操作,并且在(3)执行前开始执行(2)操作,这时候输出结果为0而不是4。

    重排序在多线程下会导致非预期的程序执行结果,而使用 volatile 修饰 ready 就可以避免重排序和内存可见性问题。

    写 volatile 变量时,可以确保 volatile 写之前的操作不会被编译器重排序到 volatile 写之后。读 vola 变量时,可以确保 volatile 读之后的操作不会被编译器重排序到 volatile 读之前。

    展开全文
  • Java指令重排序

    2019-02-15 09:14:20
    一、指令重排序 我们先看一下下面的代码会输入 什么样的结果? public class PossibleReordering { static int x = 0, y = 0; static int a = 0, b = 0; public static void main(String[] args) throws ...

    一、指令重排序
    我们先看一下下面的代码会输入 什么样的结果?

    public class PossibleReordering {
    static int x = 0, y = 0;
    static int a = 0, b = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread one = new Thread(new Runnable() {
            public void run() {
                a = 1;
                x = b;
            }
        });

        Thread other = new Thread(new Runnable() {
            public void run() {
                b = 1;
                y = a;
            }
        });
        one.start();other.start();
        one.join();other.join();
        System.out.println("(" + x + "," + y + ")");
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    很容易想到这段代码的运行结果可能为(1,0)、(0,1)或(1,1),因为线程one可以在线程two开始之前就执行完了,也可能线程two在线程one开始之前就执行完了,甚至有可能二者的指令是同时或交替执行的。

    然而,这段代码的执行结果也可能是(0,0)【参考附录代码】 . 因为,在实际运行时,代码指令可能并不是严格按照代码语句顺序执行的。得到(0,0)结果的语句执行过程,如下图所示。值得注意的是,a=1和x=b这两个语句的赋值操作的顺序被颠倒了,或者说,发生了指令“重排序”(reordering)。(事实上,输出了这一结果,并不代表一定发生了指令重排序,内存可见性问题也会导致这样的输出)

    四种结果线程执行图: 


    二、指令重排序的类型
    在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。重排序分三种类型:

    编译器优化的重排序
    编译器在不改变单线程程序语义的前提下(代码中不包含synchronized关键字),可以重新安排语句的执行顺序。

    指令级并行的重排序
    现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。

    内存系统的重排序。
    由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。 
    从java源代码到最终实际执行的指令序列,会分别经历下面三种重排序:

    上述的1属于编译器重排序,2和3属于处理器重排序。这些重排序都可能会导致多线程程序出现内存可见性问题。对于编译器,JMM的编译器重排序规则会禁止特定类型的编译器重排序(不是所有的编译器重排序都要禁止)。对于处理器重排序,JMM的处理器重排序规则会要求java编译器在生成指令序列时,插入特定类型的内存屏障(memory barriers,intel称之为memory fence)指令,通过内存屏障指令来禁止特定类型的处理器重排序(不是所有的处理器重排序都要禁止)。 
    JMM属于语言级的内存模型,它确保在不同的编译器和不同的处理器平台之上,通过禁止特定类型的编译器重排序和处理器重排序,为程序员提供一致的内存可见性保证。

    三、处理器重排序与内存屏障指令
    现代的处理器使用写缓冲区来临时保存向内存写入的数据。写缓冲区可以保证指令流水线持续运行,它可以避免由于处理器停顿下来等待向内存写入数据而产生的延迟。同时,通过以批处理的方式刷新写缓冲区,以及合并写缓冲区中对同一内存地址的多次写,可以减少对内存总线的占用。虽然写缓冲区有这么多好处,但每个处理器上的写缓冲区,仅仅对它所在的处理器可见。这个特性会对内存操作的执行顺序产生重要的影响:处理器对内存的读/写操作的执行顺序,不一定与内存实际发生的读/写操作顺序一致!我们以内存模型的的视角讲解一下上面代码出现x = 0 y =0的情况。请看下图

    这里处理器A和处理器B可以同时把共享变量写入自己的写缓冲区(A1,B1),然后从内存中读取另一个共享变量(A2,B2),最后才把自己写缓存区中保存的脏数据刷新到内存中(A3,B3)。当以这种时序执行时,程序就可以得到x = y = 0的结果。 
    从内存操作实际发生的顺序来看,直到处理器A执行A3来刷新自己的写缓存区,写操作A1才算真正执行了。虽然处理器A执行内存操作的顺序为:A1->A2,但内存操作实际发生的顺序却是:A2->A1。此时,处理器A的内存操作顺序被重排序了(处理器B的情况和处理器A一样,这里就不赘述了)。 
    这里的关键是,由于写缓冲区仅对自己的处理器可见,它会导致处理器执行内存操作的顺序可能会与内存实际的操作执行顺序不一致。由于现代的处理器都会使用写缓冲区,因此现代的处理器都会允许对写-读操作重排序。

    下面是常见处理器允许的重排序类型的列表:

    处理器/重排序类型    Load-Load    Load-Store    Store-Store    Store-Load    数据依赖
    sparc-TSO    N    N    N    Y    N
    x86    N    N    N    Y    N
    ia64    Y    Y    Y    Y    N
    PowerPC    Y    Y    Y    Y    N
    注:上表单元格中的“N”表示处理器不允许两个操作重排序,“Y”表示允许重排序。

    从上表我们可以看出:常见的处理器都允许Store-Load重排序;常见的处理器都不允许对存在数据依赖的操作做重排序。sparc-TSO和x86拥有相对较强的处理器内存模型,它们仅允许对写-读操作做重排序(因为它们都使用了写缓冲区)。

    四、JMM内存屏障指令
    内存屏障(Memory Barrier,或有时叫做内存栅栏,Memory Fence)是一种CPU指令,用于控制特定条件下的重排序和内存可见性问题。Java编译器也会根据内存屏障的规则禁止重排序。java编译器在生成指令序列的适当位置会插入内存屏障指令来禁止特定类型的处理器重排序。JMM把内存屏障指令分为下列四类:

    屏障类型    指令示例    说明
    LoadLoad Barriers    Load1; LoadLoad; Load2    确保Load1数据的装载,之前于Load2及所有后续装载指令的装载。
    StoreStore Barriers    Store1; StoreStore; Store2    确保Store1数据对其他处理器可见(刷新到内存),之前于Store2及所有后续存储指令的存储。
    LoadStore Barriers    Load1; LoadStore; Store2    确保Load1数据装载,之前于Store2及所有后续的存储指令刷新到内存。
    StoreLoad Barriers    Store1; StoreLoad; Load2    确保Store1数据对其他处理器变得可见(指刷新到内存),之前于Load2及所有后续装载指令的装载。StoreLoad Barriers会使该屏障之前的所有内存访问指令(存储和装载指令)完成之后,才执行该屏障之后的内存访问指令。
    StoreLoad Barriers是一个“全能型”的屏障,它同时具有其他三个屏障的效果。现代的多处理器大都支持该屏障(其他类型的屏障不一定被所有处理器支持)。执行该屏障开销会很昂贵,因为当前处理器通常要把写缓冲区中的数据全部刷新到内存中(buffer fully flush)。

    五、happens-before
    从JDK5开始,java使用新的JSR -133内存模型(本文除非特别说明,针对的都是JSR- 133内存模型)。JSR-133使用happens-before的概念来阐述操作之间的内存可见性。在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间。 
    与程序员密切相关的happens-before规则如下:

    程序顺序规则: 
    一个线程中的每个操作,happens- before 于该线程中的任意后续操作。

    监视器锁规则: 
    对一个监视器锁的解锁,happens- before 于随后对这个监视器锁的加锁。

    volatile变量规则:对一个volatile域的写,happens- before 于任意后续对这个volatile域的读。

    传递性: 
    如果A happens- before B,且B happens- before C,那么A happens- before C。

    注意,两个操作之间具有happens-before关系,并不意味着前一个操作必须要在后一个操作之前执行!happens-before仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前(the first is visible to and ordered before the second)。happens- before的定义很微妙,后文会具体说明happens-before为什么要这么定义。 
    happens-before与JMM的关系如下图所示:

    如上图所示,一个happens-before规则通常对应于多个编译器和处理器重排序规则。对于java程序员来说,happens-before规则简单易懂,它避免java程序员为了理解JMM提供的内存可见性保证而去学习复杂的重排序规则以及这些规则的具体实现。

    六、as-if-serial语义
    在讲as-if-serial语义之前,先说一下数据依赖性。

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

    名称    代码示例    说明
    写后读    a = 1;b = a;    写一个变量之后,再读这个位置。
    写后写    a = 1;a = 2;    写一个变量之后,再写这个变量。
    读后写    a = b;b = 1;    读一个变量之后,再写这个变量。
    注:这里所说的数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作,不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑。

    上面三种情况,只要重排序两个操作的执行顺序,程序的执行结果将会被改变。 
    前面提到过,编译器和处理器可能会对操作做重排序。编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。

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

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

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

    package demo.com.test.thread;

    /**
     * 
     * 类描述
     * Copyright © 2017永乐科技. All rights reserved.
     * <p>@Title: Test.java <p>
     * <p>@Package: demo.com.test.thread <p>
     * <p>@author: keep_trying <p>
     * <p>@date: 2017年12月19日 下午2:41:54 <p>
     * <p>@version: V1.0 <p>
     */
    public class Test {
        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
            }
        }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    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语义允许对存在控制依赖的操作做重排序的原因);但在多线程程序中,对存在控制依赖的操作重排序,可能会改变程序的执行结果。

    八、引用
    https://tech.meituan.com/java-memory-reordering.html 
    http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf 
    https://www.cs.umd.edu/users/pugh/java/memoryModel/jsr-133-faq.html 
    http://www.infoq.com/cn/articles/java-memory-model-2

    附录
    package demo.com.test.thread;

    /**
     * 
     * 类描述
     * Copyright © 2017永乐科技. All rights reserved.
     * <p>@Title: InstructionReorder.java <p>
     * <p>@Package: demo.com.test.thread <p>
     * <p>@author: keep_trying <p>
     * <p>@date: 2017年12月19日 下午1:34:07 <p>
     * <p>@version: V1.0 <p>
     */
    public class InstructionReorder {
        private static int x = 0, y = 0;
        private static int a = 0, b = 0;

        public static void main(String[] args) throws InterruptedException {
            int i = 0;
            for (;;) {
                i++;
                x = 0;
                y = 0;
                a = 0;
                b = 0;
                Thread one = new Thread(new Runnable() {
                    public void run() {
                        // 由于线程one先启动,下面这句话让它等一等线程two. 读着可根据自己电脑的实际性能适当调整等待时间.
                        shortWait(100);
                        a = 1;
                        x = b;
                    }
                });

                Thread other = new Thread(new Runnable() {
                    public void run() {
                        b = 1;
                        y = a;
                    }
                });
                one.start();
                other.start();
                one.join();
                other.join();
                String result = "第" + i + "次 (" + x + "," + y + ")";
                if (x == 0 && y == 0) {
                    System.err.println(result);
                    break;
                } else {
                    System.out.println(result);
                }
            }
        }

        public static void shortWait(long interval) {
            long start = System.nanoTime();
            long end;
            do {
                end = System.nanoTime();
            } while (start + interval >= end);
        }
    }
     

    展开全文
  • 指令重排序精讲

    2019-07-05 18:25:07
    这篇文章终于把指令重排序讲清楚了 目录: 1.数据依赖性 2.程序顺序规则 3.重排序对多线程的影响 4.编译器重排序 5.指令集并行的重排序 6.内存系统的重排序 7.memory barrier 8.JDK 1.7 内存屏障实现 1.数据依赖...
  • ZZ指令重排序

    2016-02-03 16:06:35
    ZZ http://ifeve.com/java-memory-model-2/ ZZ http://blog.csdn.net/wxwzy738/article/details/43238089 ZZ http://www.th7.cn/Program/java/201312/166504.shtml ...volatile是个很老的关键字,
  • 关于指令重排序

    2020-05-16 20:26:27
    最近在看 JVM 与高并发相关书籍,发现两本书都提到了指令重排序优化,但都没对指令重排序优化进一步解释,后面在查找了相关资料后才对指令重排序有个整体的了解,此篇文章对该部分知识做一个整理,希望对大家有所...
  • 在接触多线程的过程中,都会接触到volitile关键字,作用是禁止cpu指令重排序,和保证线程间的可见性。 如何验证cpu存在指令重排序的呢? 国外有个哥们用C做过实验,这里用java做一次 ... ... * 测试cpu的指令重排序功能...

空空如也

1 2 3 4 5 ... 20
收藏数 2,407
精华内容 962
关键字:

指令重排序