指令重排序的原理

指令重排序有两类,编译器重排序和处理器重排序。(至于内存系统指令重排较为复杂不是本章重点)

重排序分为两类:编译期重排序和运行期重排序,分别对应编译时和运行时环境。
编译器重排序发生在编译期,处理器重排序发生在运行时。其实指令重排序的本意是提高程序并发效率,原则是重排序后的程序运行结果和单线程运行结果一致。(AS IF SERIAL)

指令重排的原因
为什么指令重排序会提高程序并发效率呢?这里先理解一下CPU的最小调度单位是线程这个概念。

一个CPU同时只能处理一个线程,在最初单核CPU的时候,是通过轮询的方式去完成多线程的,在线程之间完成上下文切换。

现在都是多核CPU,其中每个CPU也是在轮询线程,只不过多核CPU并发效率更高了。

这就存在一个问题,CPU的运算速度要远快于对内存的操作,将工作内存数据写入主内存即物理内存时,如果两个CPU同时需要写入同一块内存区域,这就需要一个CPU等待另一个CPU写入完成后再写,这就造成了CPU的浪费,而这种情况在单核CPU是不存在的,所以需要指令重排序。

举个例子
int a = 1;
int b = 2;

指令排序案例分析
a和b需要写入不同的内存区域,在多线程中:

如果CPU1是先写入a到内存a,再写入b到内存b。
那么CPU2必然也是这个顺序,这就容易造成两个CPU想同时往内存a中写入,这就需要一个CPU等待另一个写入完成,这就造成了CPU的等待浪费。
但是如果线程2中指令重排序一下,变为int b = 2; int a = 1;
那么CPU2就是先写入b到内存b,再写入a到内存a。
这样两块CPU就可以同时写入,这才是真正的多核CPU,这就是指令重排序的目的。


指令排序的局限性
当然指令重排序也是有条件的,有一个语句间依赖性的概念,分为数据依赖性和控制依赖性。

数据依赖性
指后一条语句要使用上一条语句的数据

控制依赖性是指后一条语句要使用上一条语句的判断结果。语句间有依赖性就不可以指令重排序了,这也很好理解。但是在高并发中,如果不对共享变量做并发处理,指令重排序会造成严重问题。

举个例子:

如何线程a调用了write方法,并进行了指令重排序,先将b=true写入内存再将a=1写入内存,这就可能出现一种情况。

线程a将b=true写入内存后还没有来得及将a=1写入内存,此时线程b正在调用read方法,由于从内存中读到的b为true。

if(b)判断成功后去读a,此时a并没有被写入为1,所以这时候共享变量就发生的错误。

可见性
可以用volatile关键字去修饰共享变量,这时候如果该变量在工作内存中被修改,那么不需要写入主内存就对其他线程是可见的,即保证了该变量的可见性。

例如同步锁保证得到锁时从内存里重新读入数据刷新缓存,释放锁时将数据写回内存以保数据可见,而volatile变量干脆都是读写内存。

有序性
同时被volatile修饰的变量不允许被指令重排序和缓存(内存屏障)。

避免多线程中指令重排序问题需要我们的编码遵循happen-and-before原则,都是有关于并发编程加锁机制。

unlock操作,先行发生于对同一对象的lock操作,这里包括发生在其它线程中的lock操作。

volatile修饰的变量写先发生于读,这保证了该变量的可见性。

thread的start()方法先行发生于该线程的每一个动作。

thread的join()方法即终止方法后发生先于该线程的每一个动作,可以用thread.alive()方法的返回值判断该线程是否已经终止。

thread的interrupte()方法即中断方法先行发生于被中断线程的代码检测到中断事件的发生,通过thread.interrupte()方法检测线程是否已中断。

一个对象的初始化完成即其构造方法的调用结束要先行与他的终结方法例如finalize()。

动作有传递性,如果动作A先于动作B,动作B先于动作C,那么动作A先于动作C。

在并发程序中,程序员会特别关注不同进程或线程之间的数据同步,特别是多个线程同时修改同一变量时,必须采取可靠的同步或其它措施保障数据被正确地修改,这里的一条重要原则是:不要假设指令执行的顺序,你无法预知不同线程之间的指令会以何种顺序执行。

理想的模型是:各种指令执行的顺序是唯一且有序的,这个顺序就是它们被编写在代码中的顺序,与处理器或其它因素无关,这种模型被称作顺序一致性模型,也是基于冯·诺依曼体系的模型。

当然,这种假设本身是合理的,在实践中也鲜有异常发生,但事实上,没有哪个现代多处理器架构会采用这种模型,因为它是在是太低效了。而在编译优化和CPU流水线中,几乎都涉及到指令重排序。

转载自【底层原理】字节码指令重排序_51CTO博客_指令重排序 volatile

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值