指令重排是指:代码执行顺序和预期不一致。
代码运行一般步骤为:
1、从内存中获取指令解码
2、计算值
3、执行代码操作
4、把结果写回内存
而写回内存的操作比较耗时,CPU为了性能,可能不会等它完成,就进行对下一个指令解码计算。
发生指令重排是CPU为了提高性能,但必须是对结果不影响的情况,比如:
a =1; b =2; 先计算a 或者 b 对结果来说没有影响,就有肯能发送重排;而像 a = 5; b = a*3;这样的代码则不会发生重排。
一把发生指令重排在单线程中基本没啥影响,当时在多线程中,同时操作一个数据,如果一个读,一个写,当写的线程还没写回时,另一个线程已经开始读取了,那么这个时候就会出现和预期不一致的结果。
写一个可能生指令重排的代码:
public class HappenBefore {
private static int a = 0;
private static boolean flag = false;
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
a = 0;
flag = false;
// 线程1 更改数据
Thread t1 = new Thread(() -> {
a = 1;
flag = true;
});
// 线程2 读取数据
Thread t2 = new Thread(() -> {
if (flag) {
a *= 1;
}
if (a == 0) {
System.out.println("指令重排了-->" + a);
}
});
t1.start();
t2.start();
// 合并线程,让更改数据线程先运行完
t1.join();
t2.join();
}
}
}
运行以上代码:
我的电脑配的是最新的4代内存,运行10次,发生两次指令重排,如果用的是老一代的内存条,发生几率应该是会大一些。