那么要如何避免这一情况呢?我们需要给instance对象增加一个volatile关键字进行修饰,这样就不会出现指令重排的情况了。若对volatile不是很清楚的话,可以参考我另一篇文章中对volatile关键字的介绍:
package com.huawei.l00379880.mythread.Chapter04SecureObject.Section2And3SafePublish; /*********************************************************** * @note : 双重同步锁懒汉式单例+volatile禁止指令重排序 * 给instance加volatile后禁止指令重排序,从而成为线程安全的 * @author : l00379880 梁山广 * @version : V1.0 at 2019/8/29 18:39 ***********************************************************/ public class Singleton2 { /** * 单例对象,使用 volatile 关键字禁止指令重排 */ private static Singleton2 instance = null; public Singleton2() { } public static Singleton2 getInstance() { // 双重检查机制 if (instance == null) { // 同步锁 synchronized (Singleton2.class) { if (instance == null) { instance = new Singleton2(); } } } return instance; } public static void main(String[] args) { instance = new Singleton2(); } }
经过volatile的修饰,当线程A执行instance = new Singleton的时候,JVM执行顺序是什么样?始终保证是下面的顺序
:
1.memory = allocate() // 分配对象的内存空间 2.ctorInstance() // 初始化对象 3.instance = memory // 设置instance指向刚分配的内存
如此在线程B看来,instance对象的引用要么指向null,要么指向一个初始化完毕的Instance,而不会出现某个中间态,保证了安全。