单例模式

单例模式是一种简单的的设计模式。虽然简单,但是应用却是十分的广泛。所有有必要针对单例模式的知识点进行梳理。

单例模式

确保类只有一个实例,并自动实例化向整个系统提供全局的访问点。从定义上可以推断出单例模式更倾向于节约资源,利于资源的重用从而节约时间。所以应用单例模式的对象应该具有以下特点:

  • 频繁地创建、销毁且性能无法优化
  • 消耗大量资源(读取配置、产生依赖对象),可以在应用启动时直接产生一个单例对象
  • 避免资源的多重占用
  • 设置全局的访问点,优化和共享资源访问

应用场景

单例模式设计的几点考虑

  • 延迟加载
  • 线程安全
  • 并发性能
  • 序列化/反序列化安全
name 延迟加载 线程安全 并发性能 序列化/反序列化安全
饿汉式 N Y Y N
懒汉式(不加锁) Y N Y N
懒汉式(加锁) Y Y N N
DCL Y Y Y N
静态内部类 Y Y Y N
枚举 N Y Y Y

单例模式实现

  1. 饿汉式

类加载时完成初始化

  • 类加载慢

  • 获取对象速度快

public class Singleton {
    private static Singleton singleton = new Singleton();

    private Singleton(){}

    public static Singleton getInstance(){
        return singleton;
    }
}
  1. 懒汉式

获取对象的时候完成初始化,实现懒加载。但是线程不安全,高并发下会出现多个实例。

public class Singleton {
    private static Singleton instance = null;

    private Singleton(){}

    public static Singleton getInstance(){
        if( instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}
  1. 懒汉式(加锁)

线程安全的懒汉式单例模式。

public class SafeSingleton {

    private static SafeSingleton instance = null;

    private SafeSingleton(){}

    public static synchronized SafeSingleton getInstance(){
        if(instance == null){
            instance = new SafeSingleton();
        }

        return instance;
    }
}
  1. 双重检查DCL
  • synchronized同步代码块保证只创建一个对象。

  • 条件判断使对象创建过后,不在进入同步块,减少锁的粒度。

  • volatitle防止指令重排序(否则出现一个线程创建一般的对象呗另一个线程访问,造成空指针)。

public class Singleton {
    private volatile static Singleton instance;

    private Singleton(){}

    public static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  1. 静态内部类
  • 利用ClassLoader的机制保证了线程安全

  • 相比饿汉式单例模式,实现懒加载,只有显示调用SingleHolder才会触发初始化。

public class Singleton {

    private static class SingletonHolder{

        private static final Singleton instance = new Singleton();

    }

    private Singleton(){}

    public static final Singleton getInstance(){
        return SingletonHolder.instance;
    }
}
  1. 枚举
  • 线程安全(虚拟机加载枚举的时候,会使用ClassLoader的loadClass方法,这个方法使用同步代码块保证线程安全)
  • 解决反序列化破坏单例的问题:
    • 普通序列化过程中,通过反射调用默认构造函数来初始化对象,反序列化的对象是新建的
    • 枚举的反序列化不是通过构造函数实现,因此不会发生于由于反序列化导致的单例破坏问题
public enum Singleton {
    INSTANCE;

    public void getInstance(){

    }
}

如何设计优秀的单例模式

引申

  1. 双重校验DCL为什么使用volatile
  2. synchronized与volatile的区别
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容