Skip to the content.

[TOC]

单例模式

介绍

引用百度百科:

单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)

追求的目标

方式

饿汉

public class Singleton {

    private static Singleton instance = new Singleton();
 
    private Singleton() {}
 
    public static Singleton getInstance() {
        return instance;
    }
}

懒汉

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

双重检测

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

为什么要加volatile?

因为singleton = new Singleton();无法保证原子性,为什么?通常Java文件将使用javac命令编译,得到Class,我们在使用javap -c Singleton.class,将获得Jvm指令,如下:

Compiled from "Singleton.java"
public class Singleton {
  public static Singleton getSingleton();
    Code:
       0: getstatic     #2                  // Field singleton:LSingleton;
       3: ifnonnull     37
       6: ldc           #3                  // class Singleton
       8: dup
       9: astore_0
      10: monitorenter
      11: getstatic     #2                  // Field singleton:LSingleton;
      14: ifnonnull     27
      17: new           #3                  // class Singleton
      20: dup
      21: invokespecial #4                  // Method "<init>":()V
      24: putstatic     #2                  // Field singleton:LSingleton;
      27: aload_0
      28: monitorexit
      29: goto          37
      32: astore_1
      33: aload_0
      34: monitorexit
      35: aload_1
      36: athrow
      37: getstatic     #2                  // Field singleton:LSingleton;
      40: areturn
    Exception table:
       from    to  target type
          11    29    32   any
          32    35    32   any
}

熟知Synchronized原理的,很快便知道以下指令和代码是对应的:

不熟悉的也没事,我教你呀,飞机票:Synchronized

      synchronized (Singleton.class) {
        if (singleton == null) {
        	singleton = new Singleton();
        }
      }

  		10: monitorenter
      11: getstatic     #2                  // Field singleton:LSingleton;
      14: ifnonnull     27
      17: new           #3                  // class Singleton             创建一个对象,分配内存
      20: dup																//                             复制栈顶数值并将复制值压入栈顶
      21: invokespecial #4                  // Method "<init>":()V         调用超类构造方法,实例初始化方法,私有方法
      24: putstatic     #2                  // Field singleton:LSingleton; 为指定的类的静态域赋值
      27: aload_0 												  // 														 将第一个引用类型本地变量推送至栈顶
      28: monitorexit

由此可见,singleton = new Singleton();可以为分为三步:1、创建对象,分配内存;2、实例初始化;3、变量引用实例。

同时,Jvm存在指令重排序情况,那么编译器可能做以下重排序:1、创建对象,分配内存;3、变量引用实例;2、实例初始化;那么在一个实例还没初始化结束的时候,此时已经被变量引用了,那么变量访问实例将会出问题。所以此处必须加上Volatile,因为Volatile的一个重要功能,就是告诉编译器禁止指令重排序。

Volatile原理不明白?没关系,飞机票:Volatile

静态内部类

public class Singleton {
 
    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }
 
    private Singleton() {}
 
    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}	

枚举

public enum Singleton {
 
    INSTANCE;
 
}	

对比

方式 优点 缺点
饿汉 线程安全、效率高 非懒加载
懒汉 线程安全、懒加载 效率低
双重检测 线程安全、懒加载、效率高
静态内部类 线程安全、懒加载、效率高
枚举 线程安全、效率高 非懒加载

所以推荐双重检测、静态内部类的写法,实际生产中,更推荐静态内部类的写法,因为便于书写逻辑不易犯错。