单例模式的两种最佳实现

622 阅读3分钟

在23种设计模式中单例模式算是比较简单的一种设计模式了,虽然简单,但是他的实现却有很多种。下面会给出两种最佳实现和其他一些常见的实现方式。

单元素枚举

不需要延迟加载时的最佳实现

public enum Singleton {

    //每个元素就是一个单例
    INSTANCE;

    //业务方法
    public void method(){}
}

优点:

  • 能防止反射
  • 能防序列化
  • 线程安全
  • 调用效率高

缺点:

  • 不能延时加载

静态内部类

需要延迟加载时的最佳实现

public class Singleton {

    //1. 创建私有的 静态内部类
    private static class inner {
        //1.1 在静态内部类里,创建 私有的 静态的 外部类对象
        private static Singleton instance = new Singleton();
    }

    //2. 私有化构造器,禁止他人创建实例
    private Singleton() {
    }

    //3. 创建 公开的 静态的 获取实例的方法
    public static  Singleton getInstance() {
        return inner.instance;
    }
}

优点:

  • 线程安全
  • 调用效率高
  • 能延时加载

缺点:

  • 不能能防止反射
  • 不能防序列化

饿汉式

public class Singleton  {
    //1. 创建私有的 静态的内部实例
    private static Singleton instance = new Singleton();

    //2. 私有化构造器,禁止他人创建实例
    private  Singleton(){}

    //3. 创建 公开的 静态的 获取实例的方法
    public static Singleton getInstance(){
        return instance;
    }
}

优点:

  • 线程安全
  • 调用效率高

缺点:

  • 不能能防止反射
  • 不能防序列化
  • 不能延时加载

懒汉式

public class Singleton {
    //1. 创建私有的 静态的内部实例,并初始化为null
    private static  Singleton instance  = null;

    //2. 私有化构造器,禁止他人创建实例
    private Singleton() {
    }

    //3. 创建 公开的 静态的 获取实例的方法
    public static  Singleton getInstance() {
        // 先判断单例是否为空,以避免重复创建
        if( instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

优点:

  • 能延时加载 缺点:
  • 不能能防止反射
  • 不能防序列化
  • 线程不安全
  • 调用效率不高

懒汉式 + 同步锁

public class Singleton {
    //1. 创建私有的 静态的内部实例,并初始化为null
    private static  Singleton instance  = null;

    //2. 私有化构造器,禁止他人创建实例
    private Singleton() {
    }

    //3. 创建 公开的 静态的 获取实例的方法
    public static synchronized Singleton getInstance() {
        // 加入同步锁
        if (instance == null)
            instance = new Singleton();
        return instance;
    }
}

优点:

  • 能延时加载
  • 线程安全 缺点:
  • 不能能防止反射
  • 不能防序列化
  • 调用效率不高

双重校验锁(懒汉式的改进)

注意:JVM的指令重排序,可能会导致双重校验锁失效

public class Singleton {
    //1. 创建私有的 静态的内部实例,并初始化为null
    private static  Singleton instance  = null;

    //2. 私有化构造器,禁止他人创建实例
    private Singleton() {
    }

    //3. 创建 公开的 静态的 获取实例的方法
    public static  Singleton getInstance() {
        // 加入双重校验锁
        // 校验锁1:第1个if
        if( instance == null){
            synchronized (Singleton.class){
                // 校验锁2:第2个 if
                if( instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

优点:

  • 能延时加载
  • 线程安全 缺点:
  • 不能能防止反射
  • 不能防序列化
  • 调用效率不高

双重校验锁优化(规避指令重排序的影响)

使用 volatile 关键字来防止指令重排序

public class Singleton {
    //1. 创建私有的 静态的 可见的 内部实例,并初始化为null
    private static volatile Singleton instance  = null;

    //2. 私有化构造器,禁止他人创建实例
    private Singleton() {
    }

    //3. 创建 公开的 静态的 获取实例的方法
    public static  Singleton getInstance() {
        // 加入双重校验锁
        // 校验锁1:第1个if
        if( instance == null){
            synchronized (Singleton.class){
                // 校验锁2:第2个 if
                if( instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

优点:

  • 能延时加载
  • 线程安全 缺点:
  • 不能能防止反射
  • 不能防序列化
  • 调用效率不高