一、CPU 指令重排序
CPU 在运行时,为了提高运行效率会对指令进行重排序,以适应 CPU 的运行。CPU 指令重排序会遵循 as-if-serial 和 happens-before 原则。
① as-if-serial 语义:单个线程的执行结果不会发生改变。
② happens-before 语义:正确同步的多线程的执行结果不会发生改变。
as-if-serial 和 happens-before 原则,都是为了保障,在 CPU 指令重排序时,不论是单线程还是多线程的情况下,最终指令重排序后的结果都应该与代码本身的应有结果保持一致。对于单线程,可能在重排序后不会产生什么问题,但是对于多线程,这个规则可能就会产生一些问题~
CPU 指令重排序后,体现在代码层面的就是,写在后面的代码,可能在 CPU 重排序之后,就可能会先执行。
二、代码测试
测试代码
public class Test {
public static int a, b, m, n;
public static void main(String[] args) throws InterruptedException {
int cnt = 0;
Runnable r1 = () -> {
a = 1;
m = b;
};
Runnable r2 = () -> {
b = 1;
n = a;
};
while(true) {
cnt ++;
a = b = m = n = 0;
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
t1.join();
t2.join();
if(m == 0 && n == 0) {
System.err.print("第 " + cnt + " 次 " + "a = " + a + " b = " + b + " m = " + m + " n = " + n);
break;
}
System.out.println("第 " + cnt + " 次 " + "a = " + a + " b = " + b + " m = " + m + " n = " + n);
}
}
}
从测试代码中,我们可以推测一下可能产生的所有结果
可能的运行顺序~
① a = 1 m = b b = 1 n = a
② a = 1 b = 1 m = b n = a
③ a = 1 b = 1 n = a m = b
④ b = 1 n = a a = 1 m = b
⑤ b = 1 a = 1 n = a m = b
⑥ b = 1 a = 1 m = b n = a
会产生的结果
① a = 1 b = 1 m = 0 n = 1
② a = 1 b = 1 m = 1 n = 0
③ a = 1 b = 1 m = 1 n = 1
在推测的所有可能的结果中,不可能出现的结果是~
a = 1 b = 1 m = 0 n = 0
如果产生了这种结果,那么 CPU 就产生了指令重排序,执行的顺序就可能变成以下情况~
① m = b n = a a = 1 b = 1
② m = b n = a b = 1 a = 1
③ n = a m = b a = 1 b = 1
④ n = a m = b b = 1 a = 1
测试结果
经过测试后发现,当运行至 209687 次时,CPU 指令重排序,得到下图的结果
可以说明,在 CPU 运行多线程时,可能会产生 CPU 指令重排序