美文网首页
单例模式

单例模式

作者: stoneyang94 | 来源:发表于2018-05-31 22:23 被阅读0次

    简介

    1. 单例类只能有一个实例
    2. 单例类必须自己创建自己的唯一实例
    3. 单例类必须给所有其他对象提供这一实例

    特点

    • 单例模式保证整个应用中某个实例有且只有一个。
    • 有些对象我们只需要一个,比如:
      • 在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。
      • 每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。
    • 如果创造出多个实例,就会导致许多问题,比如占用过多资源,不一致的结果等

    总之,选择单例模式就是为了避免不一致状态,避免政出多头。

    实现

    一.饿汉模式--急着加载

    单例会在加载类后一开始就被初始化,即使客户端没有调用 getInstance()方法。

    • 优点
      在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的
    • 缺点
      所以饿汉式的创建方式在一些场景中将无法使用:譬如 Singleton 实例的创建是依赖参数或者配置文件的,在 getInstance() 之前必须调用某个方法设置参数给它
    public class Singleton {
        //1.构造方法私有化,不允许外部直接创建对象
        private Singleton(){
        }
    
        //2.创建类的唯一实例,使用private static修饰
        private static Singleton instance=new Singleton();
    
        //3.提供一个用于获取实例的方法,使用public static修饰
        public static Singleton getInstance(){
            return instance;
        }
    }
    

    二.懒汉式--等着加载

    这段代码简单明了,但是当有多个线程并行调用 getInstance() 的时候,就会创建多个实例。也就是说在多线程下不能正常工作。

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

    并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全

    2.1整个 getInstance() 方法设为同步

    最简单的方法是将整个 getInstance() 方法设为同步(synchronized)。

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

    虽然线程安全了,但是每次都要同步,会影响性能,毕竟大部分情况下是不需要同步的

    2.2双重检查锁定

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

    第一个if (instance == null),其实是为了解决Version2中的效率问题,只有instance为null的时候,才进入synchronized的代码段——大大减少了几率。
    第二个if (instance == null),则是跟Version2一样,是为了防止可能出现多个实例的情况。

    2.3静态内部类

    这种写法使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷。

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

    内部类

    三.枚举

    public enum Singleton{
        INSTANCE;
    }
    

    我们可以通过 Singleton.INSTANCE 来访问实例,这比调用getInstance()方法简单多了。

    • 创建枚举默认就是线程安全的,所以不需要担心double checked locking
    • 而且还能防止反序列化导致重新创建新的对象

    饿汉模式和懒汉模式的对比:

    • 初始化时机
      饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,
      而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。
    • 特点
      饿汉模式加载类时比较慢,但运行时获取对象的速度比较快,线程安全;
      懒汉模式加载类时比较快,但运行时获取对象的速度比较慢,线程不安全。

    选择

    一般情况下直接使用饿汉式就好了,如果明确要求要懒加载(lazy initialization)可以使用静态内部类,如果涉及到反序列化创建对象时会试着使用枚举的方式来实现单例。

    相关文章

      网友评论

          本文标题:单例模式

          本文链接:https://www.haomeiwen.com/subject/fujcsftx.html