zoukankan      html  css  js  c++  java
  • 设计模式-单例模式

    设计模式 单例模式

    在开发中经常会用到单例设计模式,目的就是为了在程序的整个生命周期内,只会创建一个类的实例对象,而且只要程序不被杀死,该实例对象就不会被释放。 指在 JVM 范围内。

    定义

    保证一个类仅有一个实例,并提供一个访问它的全局访问点

    优缺点

    1、由于单例模式要求在全局内只有一个实例,因而可以节省比较多的内存空间;
    2、全局只有一个接入点,可以更好地进行数据同步控制,避免多重占用;
    3、单例可长驻内存,减少系统开销。

    单例模式的应用举例:
    1、生成全局惟一的序列号;
    2、访问全局复用的惟一资源,如磁盘、总线等;
    3、单个对象占用的资源过多,如数据库等;
    4、系统全局统一管理,如Windows下的Task Manager;
    5、网站计数器。

    缺点:
    1、单例模式的扩展是比较困难的;
    2、赋于了单例以太多的职责,某种程度上违反单一职责原则(六大原则后面会讲到);
    3、单例模式是并发协作软件模块中需要最先完成的,因而其不利于测试;
    4、单例模式在某种情况下会导致“资源瓶颈”。

    实现方式:

    在单例的类中设置一个 private 静态变量instance,instance 类型为当前类,用来持有单例唯一的实例。
    将(无参数)构造器设置为 private,避免外部使用 new 构造多个实例。
    提供一个 public 的静态方法,如 getInstance,用来返回该类的唯一实例 instance。

    • 懒汉式 lazy initialization
    • 饿汉式 eager initialization
    • 双重校验锁 Double CheckLock DCL
    • 枚举
    • 静态内部类

    懒汉式

    懒汉模式是声明一个静态对象,并且在用户第一次调用getInstance时进行初始化,而上述的饿汉模式是在声明静态对象时就已经初始化,

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

    synchronized 保证 线程安全。

    延迟初始化

    饿汉式

    public class Singleton {

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

    }

    不足:

    如果构造方法中存在过多的处理,会导致加载这个类时比较慢,可能引起性能问题。
    如果使用饿汉式的话,只进行了类的装载,并没有实质的调用,会造成资源的浪费。

    Double CheckLock DCL

    public class SingleDCL {
    
        private volatile static SingleDCL singleDCL = null;
        private SingleDCL(){}
    
        public static SingleDCL getSingleDCL(){
    
            if(singleDCL == null){
    
                synchronized (SingleDCL.class){
                    if(singleDCL == null){
                        singleDCL = new SingleDCL();
                    }
                }
            }
            return singleDCL;
        }
    
    }
    
    

    假设线程A执行到了instance=new Singleton2 ()语句,这里看起来是一句代码,但实际上它并不是一个原子操作,什么是原子操作?可以看这儿(http://baike.baidu.com/item/原子操作?fr=aladdin),这句代码最终会被编译成多条汇编指令,大致做了三件事:

        (1)、给Singleton的实例分配内存;
    
        (2)、调用Singleton()的构造函数,初始化成员字段;
    
        (3)、将instance对象指向分配的内存空间(此时instance就不是null);
    
        DCL的优点:资源利用率高,第一次执行getInstance时单例对象才会被实例化,效率高。
    

    DCL模式是使用最多的单例实现方式,它能够在需要时才实例化单例对象,并且能够在绝大多数场景下保证单例对象的唯一性,除非你的代码在并发场景比较复杂或者低于jdk1.6版本下使用,否则这种方式一般能够满足需求。

    《Java并发编程实战》 p286 16.2.4 这种方式已被废弃。

    静态内部类单例

    DCL 有可能失效

    枚举单例

    写法简单是枚举单例最大的优点,枚举在Java中与普通的类是一样的,不仅能够有字段,还能够有自己的方法。最重要的是默认枚举实例的创建是线程安全的,并且在任何情况下它都是一个单例。为什么?在上述的几种单例中,在一个情况下它们会出现重新创建对象的情况,那就是反序列化。
    
    通过反序列化可以将一个单例的实例对象写到磁盘,然后再读回来,从而有效获得一个实例。即使构造函数是私有的,反序列化时依然可以通过特殊的途径去创建类的一个新的实例,相当于调用该类的构造函数。反序列化操作提供了一个很特别的钩子函数,类中具有一个私有的、被实例化的方法readResolve,这个方法可以让开发人员控制对象的反序列化。例如:上述几个实例中如果要杜绝单例对象在被饭序列化重新生成对象,那么必须加入如下方法:
    
    /**
     * 枚举单例
     * Created by Brady on 2017/5/23.
     */
    public enum SingleEnum {
        INSTANCE; 
    }
    

    容器实现单例

    在程序的初始化时,将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时也可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

        不管以哪种形式实现单例模式,他们的核心原理都是讲构造函数私有化,并且通过静态方法获取一个唯一的实例,在这个获取的过程中必须保证线程安全、防止反序列化导致重新生成实例对象等问题。选择哪种实现方式取决于项目本身,如是否是复杂的并发环境、jdk版本是否过低、单例对象的资源消耗等。
    

    参考资料

    设计模式系列 14-- 单例模式
    Java设计模式(十) 你真的用对单例模式了吗?
    Android设计模式——单例模式之源码使用场景(一)
    单例模式的七种写法
    1、Python与设计模式--单例模式

  • 相关阅读:
    aaa
    记一次Vue实战总结
    Data too long for column 'xxx' at row 1MySql.Data.MySqlClient.MySqlPacket ReadPacket() 报错解决
    uni-app 监听返回按钮
    微信H5分享外部链接,缩略图不显示
    uni-app 动态控制下拉刷新
    vueX 的使用
    uni-app H5 腾讯地图无法导航
    uni-app支付功能
    hooks 与 animejs
  • 原文地址:https://www.cnblogs.com/ironbrady/p/6961411.html
Copyright © 2011-2022 走看看