先看一个简单问题
定义了A=0,B=0;a=0,b=0四个全局变量。设计两个线程,线程1执行a=1;A=b; 线程2执行b=1;B=a;
要求一:两个线程遵循Happens-Before原则
要求二:保证两个线程都执行完后打印出A的值,B的值(注:列举出A,B的所有可能的取值组合,并解释每一种组合发生的原因)
答案:
第一种: A=0,B=1 thread1在thread2之前先执行(a=1,A=0; b=1,B=1)
第二种: A=1,B=0 thread2在thread1之前先执行(b=1,B=0; a=1,A=1)
第三种: A=1,B=1 thread1在thread2同时执行 (a=1,b=1; A=1,B=1) //这里可以使用 CountDownLatch来尽可能保证线程1的a=1;和线程2的b=1同时执行;
第四种: A=0,B=0 发生这种情况的原因只有一个,那就是线程一和线程二的代码的执行顺序发生了改变:
B=a
a=1
A=b
b=1
我们可以看出其实是线程2的代码的执行顺序发生了改变才会出现第四种情况,这就是所谓的重排序
再看代码:
public class ReorderingCode {
private static int A=0,B=0;// 这里其实可以用volatile来解决第四种情况的发生
private static int a=0,b=0;
public static void main(String[] args) throws InterruptedException {
CountDownLatch lunch=new CountDownLatch(1);
int sum=0;
for (;;){
A=0;B=0;a=0;b=0;
sum++;
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
lunch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
a=1;
A=b;
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
lunch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
b=1;
B=a;
}
});
thread1.start();
thread2.start();
lunch.countDown();
thread1.join();
thread2.join();
if (A==0&&B==0){
System.out.println("执行了"+sum+"次"+"A="+A+","+"B="+B);
break;
};
}
}
}
结论:
前三种情况出现是很正常的,但是第四种出现就不正常了,因为cpu剥夺了我们程序员的权利,它私自对我们代码的执行顺序做了调换,这就叫做重排序,但是这只是cpu层面的重排序我们可以看下图
从图中看重排序,我们便一目了然,但是发生重排序其实是很有好处的,但是我们在业务开发中一定要处理好这种情况发生的时候该怎么办来规避可能在生产中发生。