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与设计模式--单例模式

  • 相关阅读:
    hdu 5119 Happy Matt Friends
    hdu 5128 The E-pang Palace
    hdu 5131 Song Jiang's rank list
    hdu 5135 Little Zu Chongzhi's Triangles
    hdu 5137 How Many Maos Does the Guanxi Worth
    hdu 5122 K.Bro Sorting
    Human Gene Functions
    Palindrome(最长公共子序列)
    A Simple problem
    Alignment ( 最长上升(下降)子序列 )
  • 原文地址:https://www.cnblogs.com/ironbrady/p/6961411.html
Copyright © 2011-2022 走看看