参考《java编程之美》
public class Test {
public static class ReadThread extends Thread {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
if (ready) {
System.out.println(num + num);
}
System.out.println("read is over");
}
}
}
public static class WriteThread extends Thread {
public void run() {
num = 2;
ready = true;
System.out.println("write thread is over");
}
}
private static int num = 0;
private static boolean ready = false;
public static void main(String[] args) throws InterruptedException {
ReadThread rt = new ReadThread();
rt.start();
WriteThread wt = new WriteThread();
wt.start();
Thread.sleep(10);
rt.interrupt();
System.out.println("main is over");
}
}
上面有3个线程,一个主线程,一个读线程,一个写线程。读写线程之间并没有什么先后顺序。开启两个线程后,线程处于就绪状态,等待cpu调度。
然后主线程sleep10ms.目的是让读写线程充分执行结束。最后调用读线程的中断方法使读线程中断结束。
然后大家可以先自己揣摩结果是啥。
我刚开始看感觉没看懂,自己没理解。后面理解了,所以在这里记下笔记。
先来说说什么事java指令重排序:
java内存模型允许编译器和处理器对指令重排序以提高运行性能,并且只会对不存在数据依赖性的指令重排序。
然后我们先来了解数据依赖性:
int a=1;
int b=2;
int c=a+b;
这里由于变量c的值依赖于a和b的值,所以不会进行重排序,即不会变成:
int c=a+b;
int a=1;
int b=2;
这样的顺序。
然后再来看最前面的程序:
if (ready) {
System.out.println(num + num);
}
num = 2;
ready = true;
由于在两个线程中,所以这上面三个步骤是没有数据依赖性的。
所以在写线程中,它不一定会执行num=2后再让ready=true;
有可能会ready=true后再运行num=2;
如果是后面这种情况的话,读线程就可能先读到ready为true,然后默认num=0,所以直接输出0+0=0;然后cpu又继续运行num=2;此时会再输出2+2=4的这种情况。
所以,这就是指令重排序带来的灾难。
但是,你们运行上面的程序估计很难运行出0的情况,因为带有很大的不确定性。所以自己理解了就好。
java为了避免发生这种情况,可以使用volatile修饰上面的ready,这样ready前面的操作比如num=2就不会被重排序到ready后面。这样就不会发生前面的这种情况。
写volatile变量时,可以确保volatile写之前的操作不会被编译器重排序到volatile写之后。读volatile变量时,可以确保volatile读之后的操作不会被编译器重排序到volatile读之前。
上面就是我理解到的指令重排序。
欢迎交流讨论。