JVM之指令重排

1、什么是指令重排?

计算机执行指令的过程中会经过程序编译器编译形成的指令序列,一般而言,这个指令序列是会输出确定的结果;以确保每一次的执行都有确定的结果。但是,一般情况下,CPU和编译器为了提升程序执行的效率,会按照一定的规则允许进行指令优化,在某些情况下,这种优化会带来一些执行的逻辑问题,主要的原因是代码逻辑之间是存在一定的先后顺序,在并发执行情况下,会发生二义性,即按照不同的执行逻辑,会得到不同的结果信息。

2.  数据依赖性

主要指不同的程序指令之间的顺序是不允许进行交换的,即可称这些程序指令之间存在数据依赖性。

进过分析,发现这里每组指令中都有写操作,这个写操作的位置是不允许变化的,否则将带来不一样的执行结果。

编译器将不会对存在数据依赖性的程序指令进行重排,这里的依赖性仅仅指单线程情况下的数据依赖性;多线程并发情况下,此规则将失效。

3、as-if-serial语义

不管怎么重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变。编译器,runtime 和处理器都必须遵守as-if-serial语义。为了遵守as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。但是,如果操作之间不存在数据依赖关系,这些操作可能被编译器和处理器重排序。

分析:  关键词是单线程情况下,必须遵守;其余的不遵守。


4.  在多线程下的指令重排

多线程情况下,下面代码指令重排是否会产生不同的结果?

上述的代码,在单线程情况下,执行结果是确定的, flag=true将被reader的方法体中看到,并正确的设置结果。 但是在多线程情况下,是否还是只有一个确定的结果呢?

在不同的线程之间共享变量;由于执行顺序、CPU编译器对于程序指令的优化等造成了不确定的执行结果。

5、指令重排的原因

主要还是编译器以及CPU为了优化代码或者执行的效率而执行的优化操作;应用条件是单线程场景下,对于并发多线程场景下,指令重排会产生不确定的执行效果。

6、如何防止指令重排

volatile关键字可以保证变量的可见性,因为对volatile的操作都在Main Memory中,而Main Memory是被线程所共享的,这里的代价就是牺牲了性能。

volatile还有一个作用就是局部阻止重排序的发生,对volatile变量的操作指令都不会被重排序,因为如果重排序,又可能产生可见性问题。

在保证可见性方面,锁(包括显式锁、对象锁)以及对原子变量的读写都可以确保变量的可见性。但是实现方式略有不同,例如同步锁保证得到锁时从内存里重新读入数据刷新缓存,释放锁时将数据写回内存以保数据可见,而volatile变量干脆都是读写内存。

7、可见性

  这里提到的可见性是指前一条程序指令的执行结果,可以被后一条指令读到或者看到,称之为可见性。反之为不可见性。这里主要描述的是在多线程环境下,指令语句之间对于结果信息的读取即时性。

你可能感兴趣的:(JVM之指令重排)