为了提高程序执行的性能,编译器和处理器会对指令进行重新排序,但重新排序的一个前提条件是『不能改变程序在单线程状态下执行的结果』。
举个简单的例子:
1 2 3 4 5 6 7 |
int a = 10; boolean flag = true; if (flag) { int result = a * a; } else { int result = a + a; } |
第一行代码 int a = 10; 和第二行代码 boolean flag = true; 的执行顺序,就可以被『重排序』(这里只是举个例子,不是说编译器就一定会对这两行代码进行重排序),因为这两行代码的执行顺序不会影响最终的变量 result 的结果是 100,在单线程的情况下,这样的重排序是没问题的,因为最终的计算结果是正确的。
但是在多线程的情况下,上述指令重排序就可能会产生问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class TestObj { int a = 1; boolean flag = false; public void init() { a = 100; flag = true; } public void run() { if (flag) { int result = a * a; System.out.println("result的值是:" + result); } else { int result = a + a; System.out.println("result的值是:" + result); } } } |
看上面代码,单线程情况下,先调用 init() 方法设置 a = 100; 和 flag = true; ,再调用 run() 方法,init() 方法设置 a = 100; 和 flag = true; 这两行代码的顺序如果发生了重排序(即先执行 flag = true; 再执行 a = 100)也不会影响 run() 方法执行的结果(即 result 最终等于 10000),所以编译器或处理器有可能对这两行代码做重排序。
但是在多线程情况下,上面的重排序就可能产生问题,比如编译器或处理器对 a = 100; 和 flag = true; 进行重排序后变成了先 flag = true; 然后 a = 100; ,那么当线程1 执行了 flag = true; 后失去了 CPU 使用权,线程2 开始执行 run() 方法判断 flag 的值是 true 就执行了 int result = a * a; ,而此时 a = 1,所以最终 result 的结果就是 1 了。
怎么禁止这种重排序呢?我们只用用 volatile 对 flag 变量进行修饰即可,然后针对 flag 变量的操作就会按照你源码中的顺序执行,不会再对其进行重排序。
参我的文章 volatile 关键字详解 。