Java—指令重排序
指令重排序
指令概念
指令是指示计算机执行某种操作的命令,如:数据传送指令、算术运算指令、位运算指令、程序流程控制指令、串操作指令、处理器控制指令。指令不同于我们所写的代码,一行代码按照操作的逻辑可以分成多条指令。
举个例子:int a = 1; 这段代码大致可以分为两条指令:1.加载常量 1;2.将常量 1 赋值给变量 a。
指令重排序
只要程序的最终结果与它顺序化情况的结果相等,那么指令的执行顺序可以与代码逻辑顺序不一致,这个过程就叫做指令的重排序。
指令重排序的意义:使指令更加符合 CPU 的执行特性,最大限度的发挥机器的性能,提高程序的执行效率。
指令重排序分类
指令重排序主要分为三种,在这里主要讨论 JVM 中的指令重排序。
编译器重排序:JVM 中完成
指令级并行重排序
处理器重排序:CPU 中完成
指令重排序原则
如果程序中操作 A 在操作 B 之前,那么线程中操作 A 将在操作 B 之前执行。(只对指令内部重排序,不在指令间重排序)
As-If-Serial 语义
不管怎么进行指令重排序,单线程内程序的执行结果不能被改变。
编译器和处理器对存在依赖关系的操作都不会对其进行重排序。只有不存在依赖关系的操作有可能进行重排序。
Happens-Before 原则
保证正确同步的多线程程序的执行结果不被改变。
对于被同步的操作,如果操作 A 先于操作 B,那么 A 操作的执行结果将对 B 操作可见,而且 A 操作的执行顺序排在 B 操作之前。
管理锁定规则:一个 unlock 操作 happen—before 后面(时间上的先后顺序)对同一个锁的 lock 操作。 (如果线程 1 解锁了 monitor a,接着线程 2 锁定了 a,那么,线程 1 解锁 a 之前的写操作都对线程 2 可见(线程 1 和线程 2 可以是同一个线程))
防止指令重排序
volatile 关键字通过“内存屏障”来防止指令被重排序。
为了实现 volatile 的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
Java 内存模型采取保守策略(见缝就插)
在每个 volatile 写操作的前面插入一个 StoreStore 屏障。 在每个 volatile 写操作的后面插入一个 StoreLoad 屏障。 在每个 volatile 读操作的后面插入一个 LoadLoad 屏障。 在每个 volatile 读操作的后面插入一个 LoadStore 屏障。
Synchronized 把多线程执行环境改变为单线程执行环境,无需关心指令重排序(单线程执行结果不会改变)。
版权声明: 本文为 InfoQ 作者【武师叔】的原创文章。
原文链接:【http://xie.infoq.cn/article/60d136b8add9eda31aa7e8ca2】。文章转载请联系作者。
评论