我们知道,程序运行过程中,可能会发生CPU指令重排和编译器指令重排,那么程序开发过程中,会不会发生呢?
可能很多程序员在工作中基本上不会遇到,也就相当于没有直观性,那下面我们就来举个例子,代码如下:
/**
* 重排序测试
* @author 爱吃鱼的乌贼
*
*/
public class ReorderTest {
private static int a = 0;
private static int b = 0;
private static int x = 0;
private static int y = 0;
private static int i = 0;
public static void main(String[] args) throws InterruptedException {
for(;;) {
i++;
a=0;b=0;x=0;y=0;
//创建2个CyclicBarrier对象,执行完后执行当前类的run方法
CyclicBarrier cb = new CyclicBarrier(2);
Thread t1 = new Thread(new Runnable() {
public void run() {
try {
cb.await();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
a=1;
y=b;
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
try {
cb.await();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
b=1;
x=a;
}
});
t1.start();
t2.start();
//t.join()方法只会使主线程(或者说调用t.join()的线程)
//进入等待池并等待t线程执行完毕后才会被唤醒。并不影响同一时刻处在运行状态的其他线程。
t1.join();
t2.join();
String result = "第"+i+"次执行结果x="+x+";y="+y;
if(x==0&&y==0) {
System.out.println("发生了指令重排");
System.out.println(result);
break;
}else {
System.out.println(result);
}
}
}
}
这边用CyclicBarrier来实现两个线程的同步执行。正常来说我们的程序x,y只会出现如下三种情况
1,1
1,0
0,1
除非发生指令重排,导致下面的逻辑
a=1;
y=b;
b=1;
x=a;
执行顺序变为
y=b;
a=1;
x=a;
b=1;
就有可能发生
0,0
那我们拭目以待吧,执行代码!
第10765985次执行结果x=0;y=1
第10765986次执行结果x=0;y=1
第10765987次执行结果x=0;y=1
第10765988次执行结果x=0;y=1
第10765989次执行结果x=0;y=1
第10765990次执行结果x=0;y=1
第10765991次执行结果x=0;y=1
第10765992次执行结果x=0;y=1
第10765993次执行结果x=0;y=1
第10765994次执行结果x=0;y=1
第10765995次执行结果x=0;y=1
发生了指令重排
第10765996次执行结果x=0;y=0
喵的,跑了一千万次才有一次重排,所以需要耐心,最起码证明了!
那要如何解决重排问题呢,只需要在a,b加上volatile关键字就可以了,volatile可以进制重排,保证有序性和可见性!
private volatile static int a = 0;
private volatile static int b = 0;