public class OrderDemo {
private static int a = 0, b = 0;
private static int x = 0, y = 0;
public static void main(String[] args) throws InterruptedException {
int i = 0;
for (;;) {
i++;
a = 0;
b = 0;
x = 0;
y = 0;
CountDownLatch countDownLatch = new CountDownLatch(1);
Thread thread1 = new Thread(() -> {
try {
countDownLatch.await();
} catch (InterruptedException e) {
}
a = 1;
x = b;
});
Thread thread2 = new Thread(() -> {
try {
countDownLatch.await();
} catch (InterruptedException e) {
}
b = 1;
y = a;
});
thread1.start();
thread2.start();
countDownLatch.countDown();
thread1.join();
thread2.join();
String result = "第" + i + "次结果:(" + x + "," + y + ")";
if (x == 0 && y == 0) {
System.err.println(result);
break;
} else {
System.out.println(result);
}
}
}
}
……
第570次结果:(0,1)
第571次结果:(0,1)
第572次结果:(0,1)
第573次结果:(0,1)
第574次结果:(0,1)
第575次结果:(0,1)
第576次结果:(0,1)
第577次结果:(0,1)
第578次结果:(0,1)
第579次结果:(0,0)
1 有序性
为提高程序性能,编译器和CPU可能会对没有依赖关系的数据操作进行重排序。
在单线程环境下,重排序不会改变程序的运行结果。
在上面这个例子中,“a = 1”和“x = b”可以交换顺序,“b = 1”和“y = a”可以交换顺序,这样就有可能出现“x == 0 && y == 0”的情况。
2 可见性
在java中,所有共享变量都位于主内存中,各个线程有着属于自己的本地内存。各个线程通过本地内存和主内存进行交互实现对共享数据的读写。一个线程更新共享变量时,如果只将数据写入本地内存,没有及时将本地内存中的数据刷新到主内存,那么其它线程读取共享变量时读取到的是一个过期数值。
在上面这个例子中,线程1更新了a的数值,没有及时将a的数值刷新到主内存,线程2更新了b的数值,没有及时将b的数值刷新到主内存,这样就有可能出现“x == 0 && y == 0”的情况。
3 原子性
在java中,原子操作是指不会被线程调度机制中断的操作。从原子操作开始到原子操作结束,不会发生线程切换。