在解释该规则之前,我们先看一段多线程访问数据的代码例子:
public class Test1 {
private int a=1, b=2;
public void foo(){ // 线程1
a=3;
b=4;
}
public int getA(){ // 线程2
return a;
}
public int getB(){ // 线程2
return b;
}
}
上面的代码,当线程1执行foo方法的时候,线程2访问getA和getB会得到什么样的结果?
答案:
A:a=1, b=2 // 都未改变
B:a=3, b=4 // 都改变了
C:a=3, b=2 // a改变了,b未改变
D:a=1, b=4 // b改变了,a未改变
上面的A,B,C都好理解,但是D可能会出乎一些人的预料。
一些不了解JMM的同学可能会问怎么可能 b=4语句会先于 a=3 执行?
这是一个多线程之间内存可见性(Visibility)顺序不一致的问题。有两种可能会造成上面的D选项。
- Java编译器的重排序(Reording)操作有可能导致执行顺序和代码顺序不一致。
关于Reording:
Java语言规范规定了JVM要维护内部线程类似顺序化语义(within-thread as-is-serial semantics):只要程序的最终结果等同于它在严格的顺序化环境中执行的结果,那么上述所有的行为都是允许的。
简单的说:假设代码有两条语句,代码顺序是语句1先于语句2执行;那么只要语句2不依赖于语句1的结果,打乱它们的顺序对最终的结果没有影响的话,那么真正交给CPU去执行时,他们的顺序可以是没有限制的。可以允许语句2先于语句1被CPU执行,和代码中的顺序不一致。
重排序(Reordering)是JVM针对现代CPU的一种优化,Reordering后的指令会在性能上有很大提升。(不知道这种优化对于多核CPU是否更加明显,也或许和单核多核没有关系。)
因为我们例子中的两条赋值语句,并没有依赖关系,无论谁先谁后结果都是一样的,所以就可能