从简单示例看对象的创建过程, 为什么双重检查的单例模式,分析Volatile关键字不能少

编译指令 :javac Test.java
反编译指令: javap -v Test

代码

public class ObjectTest {
    int m = 8;

    public static void main(String[] args) {
        ObjectTest o = new ObjectTest();
    }
}

安装jclasslib插件后,可以看到这个类的clazz文件: 如图所示:

这个是 对象的创建过程, 这个过程中有个半初始化状态, 这里的 invokespecial 和 astore_1 这二条指令会发生指令重排,
比如一个 双重检查的单例模式:

class Singleton {
    private Singleton() {
    }

    private volatile static Singleton INSTANCE;//这里没有使用 volatile 关键字

    public static Singleton getInstance() {
        if (INSTANCE == null) {  //第1行
            synchronized (Singleton.class) {  //第2行
                if (INSTANCE == null) {  //第三行
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new Singleton(); //第四行
                }
            }
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            new Thread(()->{System.out.println(Singleton.getInstance().hashCode());}).start();

        }
    }
}

如果 没有volatile关键字,那么在 INSTANCE = new Singleton();这一行代码,new对象的时候,对应的clazz文件的 invokespecial 和 astore_1 这二条指令就有可能会发生指令重排,
先new了对象,此时对象创建处于半初始状态, 先执行了astore_1 建立了关联,后执行了 invokespecial, 就会导致, 第一行判断INSTANCE == null, 此时加锁,接下来判断 INSTANCE 为半初始化状态,并不为null, 那么这个单例模式就会失败
所以 这种双重检查的单例模式, volatile关键字不能少

posted @ 2020-09-30 15:13  死不了好气呦  阅读(169)  评论(0编辑  收藏  举报