菜鸟之路-浅谈设计模式之单例设计模式

单例设计模式

定义:确保一个类仅仅有一个实例,并且自行实例化并向整个系统提供这个实例。



单例模式是一种经常使用的软件设计模式。在它的核心结构中仅仅包括一个被称为单例的特殊类。

通过单例模式能够保证系统中一个类仅仅有一个实例并且该实例易于外界訪问,从而方便对实例个数的控制并节约系统资源。假设希望在系统中某个类的对象仅仅能存在一个,单例模式是最好的解决方式。



关于单例设计模式的动机

对于系统中的某些类来说。仅仅有一个实例非常重要,比如。一个系统中能够存在多个打印任务,可是仅仅能有一个正在工作的任务;一个系统仅仅能有一个窗体管理器或文件系统;一个系统仅仅能有一个计时工具或ID(序号)生成器。如在Windows中就仅仅能打开一个任务管理器。

假设不使用机制对窗体对象进行唯一化,将弹出多个窗体。假设这些窗体显示的内容全然一致,则是反复对象。浪费内存资源;假设这些窗体显示的内容不一致。则意味着在某一瞬间系统有多个状态,与实际不符。也会给用户带来误解,不知道哪一个才是真实的状态。

因此有时确保系统中某个对象的唯一性即一个类仅仅能有一个实例非常重要。
怎样保证一个类仅仅有一个实例而且这个实例易于被訪问呢?定义一个全局变量能够确保对象随时都能够被訪问。但不能防止我们实例化多个对象。

一个更好的解决的方法是让类自身负责保存它的唯一实例。这个类能够保证没有其它实例被创建。而且它能够提供一个訪问该实例的方法。

这就是单例模式的模式动机


关于单例设计模式的要点

单例模式的要点有三个。一是某个类仅仅能有一个实例。二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
从详细实现角度来说,就是下面三点:一是单例模式的类仅仅提供私有的构造函数,二是类定义中含有一个该类的静态私有对象,三是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。

Java实例:

当一个类的实例能够有且仅仅能够一个的时候就须要用到了。为什么仅仅须要有一个呢?有人说是为了节约内存。但这仅仅是单例模式带来的一个优点。仅仅有一个实例确实降低内存占用。但是我觉得这不是使用单例模式的理由。

我觉得使用单例模式的时机是当实例存在多个会引起程序逻辑错误的时候。比方类似有序的号码生成器这种东西。怎么能够同意一个应用上存在多个呢?


饿汉式单例

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

懒汉式单例

public class Singleton {  
    private static Singleton singleton;  
    private Singleton(){}  
      
    public static synchronized Singleton getInstance(){  
        if(singleton==null){  
            singleton = new Singleton();  
        }  
        return singleton;  
    }  
} 
可是这样的单例类型在多线程中是不安全。有可能会出现两个INSTANCE,为什么呢?
假设当唯一实例尚未创建时,有两个线程同一时候调用创建方法,那么它们同一时候没有检測到唯一实例的存在,从而同一时候各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。 解决问题的办法是为指示类是否已经实例化的变量提供一个相互排斥锁(详见双重锁单例。尽管这样会减少效率)。

比較:
饿汉式是线程安全的,在类创建的同一时候就已经创建好一个静态的对象供系统使用,以后不在改变。
懒汉式适合单线程。多线程情况下假设在创建实例对象时不加上synchronized则会导致对对象的訪问不是线程安全的。
从实现方式来讲他们最大的差别就是懒汉式是延时载入, 
他是在须要的时候才创建对象,而饿汉式在载入类时创建实例。 
 饿汉式无需关注多线程问题、写法简单明了、能用则用。可是它是载入类时创建实例、所以假设是一个工厂模式、缓存了非常多实例、那么就得考虑效率问题,由于这个类一载入则把全部实例无论用不用一块创建。 懒汉式的长处是延时载入、缺点是应该用同步。

单例模式的长处:
在内存中仅仅有一个对象。节省内存空间。
避免频繁的创建销毁对象,能够提高性能。
避免对共享资源的多重占用。
能够全局訪问。




适用场景:

因为单例模式的以上长处,所以是编程中用的比較多的一种设计模式。

我总结了一下我所知道的适合使用单例模式的场景:
须要频繁实例化然后销毁的对象。
创建对象时耗时过多或者耗资源过多,但又经经常使用到的对象。


有状态的工具类对象。
频繁訪问数据库或文件的对象。
以及其它我没用过的全部要求仅仅有一个对象的场景。


单例模式注意事项:
仅仅能使用单例类提供的方法得到单例对象,不要使用反射。否则将会实例化一个新对象。


不要做断开单例类对象与类中静态引用的危急操作。
多线程使用单例使用共享资源时,注意线程安全问题。

双重锁形式单例(懒汉式进阶版。哈哈)

 public static class Singleton{
    private static Singleton instance=null;
    private Singleton(){
        //do something
    }
    public static Singleton getInstance(){
        if(instance==null){
            synchronized(Singleton.class){
                if(null==instance){
                    instance=new Singleton();
                }
            }
        }
        return instance;
    }
}
(有些朋友搞不懂为什么要推断两次Instance==null,由于在多线程中第一次推断时可能有两个或者多个instance==null。那么在synchronized锁里第一个instance已经new出来了,第二个或者后面进入的假设不推断就会反复new对象出来,所以在里面多一层推断确保Instance实例仅仅有一个)
这样还是有一个缺点就是:就是在一个线程还未全然初始化该对象时,而那个变量已经显示为被初始化,那么其它线程可能去使用这个未被全然初始化的实例,造成系统的崩溃。

只是这个在java5以上能够安全执行。


第二种完美实现的实现既线程安全又延迟载入的模式(Initialization on demand holder)使用静态内部类  演示样例:

Public class Singleton{
    Private Singleton(){};
    Public static class Singleton1{
        Private static final Singleton instance = new Singleton();
}    
    Public static Singleton getInstance(){
        Return Singleton1.instance;
}
}

这样就能保证在第一次调用getInstance()方法时,才会去初始化instance实例,并且该实例被定义为static,仅仅会被初始化一次。(这样的方法是网上看的,我还未用过,以后能够试试。哈哈大笑)

此文由本人从网上浏览总结而出。如需转载,请注明出处,谢谢!





当一个类的实例能够有且仅仅能够一个的时候就须要用到了。为什么仅仅须要有一个呢?有人说是为了节约内存,但这仅仅是单例模式带来的一个优点。仅仅有一个实例确实降低内存占用,但是我觉得这不是使用单例模式的理由。我觉得使用单例模式的时机是当实例存在多个会引起程序逻辑错误的时候。比方类似有序的号码生成器这种东西,怎么能够同意一个应用上存在多个呢?
posted @ 2017-06-21 18:06  yfceshi  阅读(158)  评论(0编辑  收藏  举报