假设在类中,定义这样的4个变量。
private static int x = 0, y = 0;
private static int a = 0, b = 0;
两行代码分别由两个线程进行如下的操作。
线程1:
a = 1;
x = b;
线程2:
b = 1;
y = a;
在一般的情况下,由于两个线程的快慢顺序前后顺序,均可能出现如下情况。
发生碰撞:a=1,b=1,x=b=1,y=a=1。输出x=1,y=1.
线程1先执行,2后执行:a=1,x=b(默认0),b=1,y=a=1.输出x=0,y=1.
线程2先执行,1后执行。b=1,y=a(默认0),a=1,x=b=1.输出x=1,y=0.
贴出代码试试。
private static int x = 0, y = 0;
private static int a = 0, b = 0;
public static void main(String[] args) throws InterruptedException {
Thread one = new Thread(new Runnable() {
@Override
public void run() {
a = 1;
x = b;
}
});
Thread two = new Thread(new Runnable() {
@Override
public void run() {
b = 1;
y = a;
}
});
two.start();
one.start();//或者1先开始 2后开始
one.join();
two.join();
System.out.println("x="+x+",y="+y);
}
然而x=1,y=1.的情况需要严格控制线程的同事到达。
对代码进行一下改造。
private static int x = 0, y = 0;
private static int a = 0, b = 0;
public static void main(String[] args) throws InterruptedException {
int i = 0;
for (; ; ) {
i++;
x = 0;
y = 0;
a = 0;
b = 0;
CountDownLatch latch = new CountDownLatch(3);
Thread one = new Thread(new Runnable() {
@Override
public void run() {
try {
latch.countDown();
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
a = 1;
x = b;
}
});
Thread two = new Thread(new Runnable() {
@Override
public void run() {
try {
latch.countDown();
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
b = 1;
y = a;
}
});
two.start();
one.start();
latch.countDown();
one.join();
two.join();
String result = "第" + i + "次(" + x + "," + y + ")";
if (x == 1 && y == 1) {
System.out.println(result);
break;
} else {
System.out.println(result);
}
}
}
可以复制代码自己去试试,会在一定情况下发生。免得自己一次次点的麻烦,用for循环。
我这次在两百多次。
第234次(0,1)
第235次(0,1)
第236次(0,1)
第237次(0,1)
第238次(0,1)
第239次(0,1)
第240次(1,1)
以上都是可以自己推断出来的,有没有想过代码被编译器或者CPU重排序,出现以下结果?
x=0,y=0.
代码被重排序,可能是这样的顺序。代码的执行顺序完全与我们编写的顺序不同。
x = b;
a = 1;
线程2:
y = a;
b = 1;
第1652次(0,1)
第1653次(0,1)
第1654次(0,1)
第1655次(0,1)
第1656次(0,1)
第1657次(0,1)
第1658次(0,1)
第1659次(0,1)
第1660次(0,1)
第1661次(0,1)
第1662次(0,0)
有时候十万次出不来,有时候几千次就行了。
这里只是演示代码重排序,不再对代码重排序做过多赘述。