这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战」
在java代码执行的过程当中,如果两个语句操作之间不存在依赖关系,是有可能会被乱序执行的,这是因为指令重排序的原因。
这也意味着,一个对象的创建在多线程情况下是可能有问题的。
public class InstanceDemo {
private static InstanceDemo INSTANCE;
private InstanceDemo() {
}
public static InstanceDemo getInstance() {
if (INSTANCE == null) {
//双重检查
synchronized (Mgr06.class) {
if(INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr06();
}
}
}
return INSTANCE;
}
public void m() {
System.out.println("m");
}
public static void main(String[] args) {
for(int i=0; i<100; i++) {
new Thread(()->{
System.out.println(InstanceDemo.getInstance().hashCode());
}).start();
}
}
}
这是单例模式中double check的典型实现
分析原因
public class CommandDisOrderTest001 {
public static void main(String[] args) {
Object object =new Object();
}
}
以下为创建一个Object对象的指令集
指令在栈中的执行的流程如下图
0 new #2 <java/lang/Object> 3 dup 4 invokespecial #1 <java/lang/Object.> 7 astore_1 8 return
- new 指令是向内存申请这个对象需要的空间,然后给对象赋予默认
- dup指令 是将在内存中的对象复制一份并且放在栈顶
- invokespecial 指令 是执行对象的初始化方法
- 把栈中的对象的引用赋予给局部变量表中的object
这样看是没问题的,但如果发生了指令重排序,指令7先执行,指令4后执行的呢? 那是不是意味着某个时刻是有可能得到一个半初始化状态的对象呢(只有默认值)?
回到之前的单例代码中,看INSTANCE = new InstanceDemo();这句话,如果指令被重排序,那么在多线程情况下,另外一个线程很可能会得到一个半初始化状态的对象!
也就是说代码在执行的过程并非如你想象,是具有一定顺序的执行流程
解决方案
有没有解决方法? 自然是有的,只需要给属性值赋予volatile的修饰,就可以避免指令重排序了。
private static volatile InstanceDemo INSTANCE;