单例模式五种实现方式以及在JDK中的体现

单例模式五种实现方式以及在JDK中的体现

一、五种实现方式

1、饿汉式

  • 构造私有
  • 提供一个静态私有的成员常量,类型就是单例类型,值是用私有构造创造出来的唯一实例
  • 提供公共的静态方法获取上述的静态成员常量
java 复制代码
public class Singleton1 implements Serializable {
    //(1)构造私有
    private Singleton1(){
        //添加if判断防止单例模式被破坏
        if(INSTANCE != null) {
            throw new RuntimeException("单例构造器禁止反射调用");
        }
        System.out.println("private Singleton1()");
    }
    //(2)提供一个静态私有的成员常量,类型就是单例类型,值是用私有构造创造出来的唯一实例
    private static final Singleton1 INSTANCE = new Singleton1();
    //(3)提供公共的静态方法获取上述的静态成员常量
    public static Singleton1 getInstance() {
        return INSTANCE;
    }
}

注意:对应所有的单例模式实现方式,如果实现Serializable接口,可能会被反射,反序列化等操作破坏单例,

2、枚举饿汉式

  • 枚举类,只定义一个变量
  • 构造私有
  • 提供公共的静态方法获取枚举常量
java 复制代码
public enum Singleton2 {
    //(1)枚举类,只定义一个变量
    INSTANCE;
    //(2)构造私有
    private Singleton2(){
        System.out.println("private Singleton3()");
    }
    //(3)获取枚举常量
    private static Singleton2 getInstance(){
        return INSTANCE;
    }

}

3、懒汉式

  • 构造私有
  • 提供一个静态私有的成员常量,类型就是单例类型,值为空
  • 当调用该方法时才new
  • 提供公共的静态方法获取上述的静态成员常量
java 复制代码
public class Singleton3 {
    //(2)声明静态变量为空
    private static Singleton3 INSTANCE = null;
    //(1)构造私有
    private Singleton3(){
        System.out.println("private Singleton1()");
    }
    //(3)提供公共的静态方法获取上述的静态成员变量
    //添加synchronized保证在多线程情况下运行正常
    public static synchronized Singleton3 getInstance() {
        //(4)调用该方法时再new,通过判断是否为空可控制只创建一个实例
        if(INSTANCE == null) {
            INSTANCE=new Singleton3();
        }
        return INSTANCE;
    }
}

注意:如果在多线程下,要防止多次创建实例,则在静态方法上添加synchronized,但其实我们只要在第一次调用时保证线程安全就可以,因此此方式的效率比较低,如果要提高效率,可以参考下述两种实现方式。

4、DCL懒汉式-双检索懒汉式

DCL懒汉式是为了保证多线程下运行正常,同时提高效率,也就是在静态方法中加锁之前判断是否已经创建了实例。同时静态变量使用volatile修饰

java 复制代码
public class Singleton4 {
    //(2)声明静态变量为空
    private static volatile Singleton4 INSTANCE = null;//可见性,有序性
    //(1)构造私有
    private Singleton4(){
        System.out.println("private Singleton1()");
    }
    //(3)提供公共的静态方法获取上述的静态成员变量,调用该方法时再new
    public static Singleton4 getInstance() {
        //在加锁之前进行判断
        if(INSTANCE == null) {//一次检索
            synchronized (Singleton4.class){
                if (INSTANCE == null){//二次检索
                    INSTANCE=new Singleton4();
                }
            }
        }
        return INSTANCE;
    }
}

5、懒汉式内部类单例模式

使用内部类的方式可以保证线程安全,建议使用

java 复制代码
public class Singleton5 {
    //构造私有
    private Singleton5(){
        System.out.println("private Singleton5()");
    }
    //创建内部类,在内部类中新建实例
    private static class Holder{
        static Singleton5 INSTANCE=new Singleton5();
    }
    private static Singleton5 getInstance(){
        return Holder.INSTANCE;
    }
}

二、单例模式在jdk中的体现

  • Runtime类是单例模式,饿汉式单例实现
  • System类中的有一个Console类型的cons变量,采用DCL懒汉式方式实现
  • Collections类中有大量单例模式实现,采用内部类懒汉式实现