一、指令重排
指令重排序是指源码顺序和程序顺序不一样,或者说程序顺序和执行的顺序不一致,重排序的对象是指令。指令重排的意义是提升系统运行效率。以下是指令重排的证明代码。如果两个线程内部是按顺序执行的那么x和y不会出现同时为0的组合。
public class T04_Disorder {
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;
Thread one = new Thread(new Runnable() {
public void run() {
//由于线程one先启动,下面这句话让它等一等线程two. 读着可根据自己电脑的实际性能适当调整等待时间.
//shortWait(100000);
a = 1;
x = b;
}
});
Thread other = new Thread(new Runnable() {
public void run() {
b = 1;
y = a;
}
});
one.start();other.start();
one.join();other.join();
String result = "第" + i + "次 (" + x + "," + y + ")";
if(x == 0 && y == 0) {
System.err.println(result);
break;
} else {
//System.out.println(result);
}
}
}
public static void shortWait(long interval){
long start = System.nanoTime();
long end;
do{
end = System.nanoTime();
}while(start + interval >= end);
}
}
运行结果如下,证明指令重排了。
二、 半初始化状态
再介绍半初始化状态时我们需要先重温一下,一个类初始化的流程,其流程如下图。半初始化就发生再linking的preparationg阶段。
对象初始化的指令如下图,当初始化命令执行到第三句时对象才算初始化完毕。new是创建一个对象,dup指令是给对象赋默认值,invokespecial指令在这里是给对象赋初始值,astore指令建立了引用连接。如果按照new->dup->invokespecial->astore的顺序执行那么整个对象的初始化过程就不会有什么问题。但是有时候指令会乱序执行执行顺序会变成new->dup->astore->invokespecial如果发生了这类情况就叫做类的半初始化,此时对象内的值是默认值而不是初始值,并且其它线程是可以获取到该对象的,这时候就会导致错误。
参考文章:
volatile的作用,线程可见,禁止指令重排序_半初始化状态 demo_qq_42687144的博客-CSDN博客
java创建对象过程 实例化和初始化_java叶新东老师的博客-CSDN博客