设计模式-单例模式

来源:互联网 发布:kmp算法next计算例题 编辑:程序博客网 时间:2024/05/20 23:55

定义

Singleton(单例)是设计模式的一种,为了保证一个类仅有一个实例,并提供一个访问它的全局访问点。

产生的原因

对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。

适用场景

一些资源管理器常常设计成单例模式。

在计算机系统中,需要管理的资源包括软件外部资源,譬如每台计算机可以有若干个打印机,但只能有一个Printer Spooler, 以避免两个打印作业同时输出到打印机中。每台计算机可以有若干传真卡,但是只应该有一个软件负责管理传真卡,以避免出现两份传真作业同时传到传真卡中的情况。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。
需要管理的资源包括软件内部资源,譬如,大多数的软件都有一个(甚至多个)属性(properties)文件存放系统配置。这样的系统应当由一个对象来管理一个属性文件。

需要管理的软件内部资源也包括譬如负责记录网站来访人数的部件,记录软件系统内部事件、出错信息的部件,或是对系统的表现进行检查的部件等。这些部件都必须集中管理,不可整出多头。

这些资源管理器构件必须只有一个实例,这是其一;
它们必须自行初始化,这是其二;
允许整个系统访问自己这是其三。
因此,它们都满足单例模式的条件,是单例模式的应用。

如何创建

如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。

优点

一、实例控制

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

二、灵活性

因为类控制了实例化过程,所以类可以灵活更改实例化过程。

缺点

一、开销

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

二、可能的开发混淆

使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。

三、对象生存期

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


单例模式有一下特点:
1 、单例类只能有一个实例。
2 、单例类必须自己自己创建自己的唯一实例。
3 、单例类必须给所有其他对象提供这一实例。

单例模式的几种形式

懒汉式(懒加载,线程不安全)

这种写法懒加载很明显,但是致命的是在多线程不能正常工作。

package demo20;/** *  * @ClassName: SingletonDemo * @Description:单例模式演示 懒汉式 * @author cheng * @date 2017-8-24 下午05:05:14 */public class SingletonDemo {    // 持有本类对象的引用    private static SingletonDemo singletonDemo;    // 私有化构造函数,不允许外界通过构造函数创建该类的对象    private SingletonDemo() {    }    // 提供一个外界获取本类实例的方法    public static SingletonDemo newInstance() {        if (singletonDemo == null) {            singletonDemo = new SingletonDemo();        }        return singletonDemo;    }}

懒汉式(懒加载,线程安全,效率低)

这种写法能够在多线程中很好的工作,而且看起来它也具备很好的懒加载,但是,遗憾的是,效率很低,99%情况下不需要同步。

package demo20;/** *  * @ClassName: SingletonDemo3 * @Description: * @author cheng * @date 2017-8-25 上午09:13:51 */public class SingletonDemo3 {    // 持有本类对象的引用    private static SingletonDemo3 singletonDemo3;    // 私有化构造函数,不允许外界通过构造函数创建该类的对象    private SingletonDemo3() {    }    // 提供一个外界获取本类实例的方法    public static synchronized SingletonDemo3 newInstance() {        if (singletonDemo3 == null) {            singletonDemo3 = new SingletonDemo3();        }        return singletonDemo3;    }}

饿汉式(没有懒加载,线程安全)

这种方式基于类加载机制避免了多线程的同步问题,不过,singletonDemo1 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用newInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化singletonDemo1 显然没有达到懒加载的效果。

package demo20;/** *  * @ClassName: SingletonDemo1 * @Description:单例模式演示 饿汉式 * @author cheng * @date 2017-8-24 下午05:16:34 */public class SingletonDemo1 {    // 持有本类对象的引用    private static SingletonDemo1 singletonDemo1 = new SingletonDemo1();    // 私有化构造函数,不允许外界通过构造函数创建该类的对象    private SingletonDemo1() {    }    // 提供一个外界获取本类实例的方法    public static SingletonDemo1 newInstance() {        return singletonDemo1;    }}

静态内部类(懒加载,线程安全)

这种方式同样利用了类加载的机制来保证初始化singletonDemo2 时只有一个线程,这种方式是SingletonDemo2 类被装载了,singletonDemo2 不一定被初始化。因为SingletonDemo2InClass 类没有被主动使用,只有显示通过调用newInstance方法时,才会显示装载SingletonDemo2InClass 类,从而实例化singletonDemo2 。想象一下,如果实例化singletonDemo2 很消耗资源,我想让他延迟加载,另外一方面,我不希望在SingletonDemo2 类加载时就实例化,因为我不能确保SingletonDemo2 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化singletonDemo2 显然是不合适的。

package demo20;/** *  * @ClassName: SingletonDemo2 * @Description: * @author cheng * @date 2017-8-25 上午09:07:55 */public class SingletonDemo2 {    // 私有化构造函数,不允许外界通过构造函数创建该类的对象    private SingletonDemo2() {    }    /**     *      * @ClassName: SingletonDemo2InClass     * @Description:静态内部类     * @author cheng     * @date 2017-8-25 上午09:13:06     */    private static class SingletonDemo2InClass {        private static SingletonDemo2 singletonDemo2 = new SingletonDemo2();    }    // 提供一个外界获取本类实例的方法    public static SingletonDemo2 newInstance() {        return SingletonDemo2InClass.singletonDemo2;    }}

枚举(线程安全,提倡)

这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊

package demo20;public enum SingletonDemo4 {    INSTANCE;    private SingletonDemo4() {    }}

双重校验锁(会出现编译错误)

思路很简单,就是我们只需要同步(synchronize)初始化singletonDemo5的那部分代码从而使代码既正确又很有效率。
很可惜,这样的写法在很多平台和优化编译器上是错误的。

package demo20;public class SingletonDemo5 {    // 持有本类对象的引用    private volatile static SingletonDemo5 singletonDemo5;    // 提供一个外界获取本类实例的方法    private SingletonDemo5() {    }    // 提供一个外界获取本类实例的方法    public static synchronized SingletonDemo5 newInstance() {        if (singletonDemo5 == null) {            synchronized (SingletonDemo4.class) {                if (singletonDemo5 == null) {                    singletonDemo5 = new SingletonDemo5();                }            }        }        return singletonDemo5;    }}
原创粉丝点击