1.什么是指令重排
指令重排是计算机编译器或处理器为了提高性能而对指令执行顺序进行的一种优化手段。在多核和多线程的计算机系统中,指令重排的目标是通过优化执行顺序来提高指令级别的并行度,充分发挥计算资源,加速程序的执行。
2.指令重排的原理
2.1 数据依赖性:
在指令重排时,编译器和处理器会尽量保留程序的语义,即确保不会改变程序的原始语义。这就要求在进行重排时要考虑数据的依赖关系,保证对于具有依赖关系的指令不会改变其执行顺序。
2.2 重排序的类型:
指令重排主要包括编译器重排和处理器重排两种类型。编译器重排指的是编译器在生成目标代码时进行的优化,而处理器重排则是指在指令执行阶段,由于处理器的乱序执行机制,实际执行的顺序可能与源代码中的顺序不同。
3.指令重排的影响
3.1 内存屏障
内存屏障(Memory Barrier)是一个CPU指令,用于禁止特定类型的指令重排。volatile
关键字和synchronized
块都会引入内存屏障,确保在其前后的指令不会发生重排。
原理:
由于编译器和处理器都能执行指令重排优化。如果在指令间插入一条内存屏障则会告诉编译器和CPU,不管什么指令都不能和这条内存屏障指令重排,也就是说通过插入内存屏障,就能禁止在内存屏障前后的指令执行重排优化。内存屏障另外一个作用就是强制刷出各种CPU的缓存数据,因此任何CPU上的线程都能读取到这些数据的最新版本。【volatile
体现】
3.2 多线程同步问题
在没有适当同步的情况下,一个线程在写入共享变量后,其它线程可能读到一个尚未完成初始化的对象,导致不一致的状态。
例如:
// 线程1
instance = new SomeObject(); // 1
ready = true; // 2
// 线程2
if (ready) {
// 可能读到尚未完成初始化的 instance
use(instance);
}
在上述例子中,由于指令重排,线程2可能在检测到ready
为true
时读取到尚未完成初始化的instance
。