指令重排序

334 阅读2分钟

我们来关注下面这段代码,假设分别有两个线程,分别执行executeToCPU0和executeToCPU1,分别 由两个不同的CPU来执行。 2和3是CPU层面;1是编译器层面。

在CPU的高速缓存下,引入Store Bufferes(为啥引入它,是因为很早之前的操作系统中CPU0更新缓存行的数据之后就会发一个read invalidate的命令,然后阻塞等待invalidate ack,这是一个阻塞,为了防止CPU阻塞,就引入了Store Bufferes)之后,就可能出现 b==1返回true ,但是assert(a==1)返回false。这其实就是引发的指令重排序的问题。 (导致CPU的重排序的原因不仅是引入的Store Bufferes的原因,也有寄存器的原因[也是为了提高CPU的执行效率,具体参考:])

executeToCPU0(){
    a=1;
	b=1; 
}
executeToCPU1(){
    while(b==1){
        assert(a==1);
    }
}

那怎么解决这指令重排序的问题呢?

通过内存屏障禁止了指令重排序

JMM(Java内存模型)是操作系统,高速缓存上抽象出来的一层,提供了一些指令(比如volatile,final关键字)来防止指令重排序,禁止高速缓存。 【volatile通过JMM在不同的平台(Linux,windows)生成不同的内存屏障的指令】 【volatile只能保证可见性,但是保证不了原子性】 参考:

彻底搞懂多线程中的volatile!

其实通过前面的内容分析我们发现,导致可见性问题有两个因素,一个是高速缓存导致的可见性问题,

另一个是指令重排序。那JMM是如何解决可见性和有序性问题的呢? 其实前面在分析硬件层面的内容时,已经提到过了,对于缓存一致性问题,有总线锁和缓存锁,缓存锁 是基于MESI协议。而对于指令重排序,硬件层面提供了内存屏障指令。而JMM在这个基础上提供了 volatile、final等关键字,使得开发者可以在合适的时候增加相应相应的关键字来禁止高速缓存和禁止 指令重排序来解决可见性和有序性问题。

---------------------其他---------

image.png

----Java多线程编程基础三(原子性,可见性和有序性)----

Java多线程编程基础三(原子性,可见性和有序性)