浅析java中的单例模式

我们在代码中最常见的单例写法可能就是下面的:

static class Singleton {
  private static Singleton singleton = null;
   public static Singleton getInstance(){
      if (singleton==null) {
          synchronized (Singleton.class) {
              if(singleton==null) {
                        singleton = new Singleton();
               }
           }
       }
      return singleton;
    }
}

这段代码的目的是对 Singleton 延迟初始化。但是每次访问的时候都需要同步。为了减少同步的开销,于是有了双重检查模式。
然而,在多个线程同时进入该方法时,如果出现了指令重排序,则会出现某个线程拿到的实例为未初始化好的对象,为什么呢? 看下面:

先看一下下图:

image.png

图中为加了volatile之后通过反编译得到了字节码,然后进行分析,从第一行开始分析:
首先获取当前的static变量Singleton, 然后判断singleton是否为null,如果不为null,则执行37行(这个时候已经初始化好了),然后return
假设线程1 进入 第10行会到synchronized锁的代码,然后再获取当前singleton的值,如果为null(14行),则调到27行,退出锁;如果不为null,则会拷贝当前对象(17),然后调用构造方法(21行),设置到变量(24行),那么,此时线程2会进行等待,当线程1退出锁之后线程2再进入 synchronized内,如果不加volatile呢?这个时候21行和24行可能会发生重排序,线程1执行到putstatic(24行)的时候,线程2刚好到ifnonnull(第3行),因此这个时候线程2不会进行等待(因为singleton对象不为null,但是该对象又未初始化完毕),此时线程2拿到的单例为一个空对象~

image.png

# 设计模式  

评论

公众号:mumuser

企鹅群:932154986

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×