单例模式学习总结

来源:互联网 发布:视频分析软件 编辑:程序博客网 时间:2024/06/05 16:42

定义

单例模式(singleton)是一种常用的、最简单的设计模式,通过单例模式可以保证该类在系统中只有一个实例,即一个类只有一个对象。


要点

1.一个类只能有一个实例-单例模式的类只提供私有(private)的构造函数。

2.类必须自行创建实例-类定义中含有一个该类的静态私有对象(private static)

3.必须向整个系统提供这个实例-该类提供了一个静态公有函数(public static)用于创建或获取它本身的静态私有对象(private static)


优点

1.单例模式会阻止其他对象实例化该类的单例对象的副本,从而确保所有对象都访问唯一的实例。

2.类自身控制了实例化过程,所以类可以灵活的改变自身的实例化过程。


缺点

1.虽然数量很少,但是每次对象请求引用时都要检查是否存在该类的实例,任然需要一些开销。可以通过使用静态初始化解决此问题。

2.使用单例对象时,必须记住不能使用new关键字实例化对象,因为可能无法访问库源代码,因此可能会发现无法直接实例化此类。

3.不能解决删除单个对象的问题,在提供内存管理的语言中(.NET Framework),只有单例类能导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用。


要素

1.私有构造方法

2.私有静态引用指向自己的实例

3.以自己实例为返回值的公有静态方法。


7种写法

1.饿汉模式

这种做法在类加载的时候就完成了初始化,所以类加载较慢,但获取对象的速度快。

这种方式基于类加载机制避免了多线程的同步问题,单页不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候的初始化显然没有达到懒加载的效果。

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

2.懒汉模式(线程不安全)

懒汉模式声明了一个静态对象,在用户第一次调用时初始化,虽然节约了资源,但是第一次加载时需要实例化,反应稍慢一点,而且多线程的时候无法正常工作

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

3.懒汉模式(线程安全)

这种做法能够在多线程中很好的工作。但是每次调用getInstance方法时都需要进行同步,造成不必要的同步开销,而且大多数情况下我们是用不到同步的,所以不建议用这种模式。

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

4.双重检查模式(DCL)

这种写法在getInstance方法中对singleton进行了两次判空,第一次是为了不必要的同步,第二次是在singleton等于null的情况下才创建实例,在这里用到了volatile关键词字,会或多或少影响性能,但考虑到程序的正确性,牺牲这点是值得的。DCL有点是资源利用率高,第一次执行getInstance时单例对象才被实例化,效率高。缺点是第一次加载时反应稍微慢一点,在高并发环境下也有一定的缺陷,虽然发生的概率很小。DCL虽然在一定程度解决了资源的消耗和多余的同步、线程安全等问题,但是还是在某些情况下出现失效的问题,也就是DCL失效。建议用静态内部类单例模式来替代DCL。

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

5.静态内部类单例模式

第一次加载类时不会初始化类对象。只有第一次调用getInstance方法时,虚拟机才会加载类并初始化类对象。这样不仅能确保线程安全,也能保证单例类的唯一性,推荐使用。

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

6.枚举单例

默认枚举实例的创建是线程安全的,并且在任何情况下都是单例。上述几种单例模式的实现中,有一种情况下他们会重新创建对象,那就是反序列化,讲一个单例实例对象写到磁盘中再读回来,从而获得了一个实例。反序列化操作提供了readResolve方法,这个方法可以控制对象的反序列化。在上述的几个方法示例中如果要杜绝单例对象被反序列化重新生成对象,就必须加入代码2。

枚举单例的优点是简单,但是很少被应用,可读性并不是很高,不建议使用。

代码1:

public enum Singleton {    INSTANCE;    public void doSomeThing() {    }}  
代码2:

private Object readResolve() throws ObjectStreamException {    return singleton;}

7.使用容器实现单例模式

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

public class SingletonManager {       private static Map<String, Object> objMap = new HashMap<String, Object>();      private Singleton() {   }      public static void registerService(String key, Objectinstance) {    if (!objMap.containsKey(key)) {      objMap.put(key, instance);    }  }      public static ObjectgetService(String key) {    return objMap.get(key);  }}

适用场景

单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如: 

1.需要频繁实例化然后销毁的对象。 

2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 

        3.有状态的工具类对象。 

        4.频繁访问数据库或文件的对象。 


应用场景

1.外部资源:每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。内部资源:大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件 

    2. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~ 

    3. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。 

    4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。 

    5. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。 

    6. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。 

    7. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。 

    8. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。 

    9. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。 

    10. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例. 

参考文档

1.https://baike.baidu.com/item/单例模式/5946627?fr=aladdin
2.http://blog.csdn.net/itachi85/article/details/50510124
3.https://www.cnblogs.com/damsoft/p/6105122.html