单例模式

一、单例模式概念

  单例模式(singleton pattern):是一种常用的软件设计模式。在它的核心结构中包含一个被称为单例的特殊类。应用该模式的类只能产生一个对象的实例。

二、单例模式的应用场景  

  对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
  在计算机系统中,需要管理的资源包括软件外部资源,譬如每台计算机可以有若干个打印机,但只能有一个Printer Spooler, 以避免两个打印作业同时输出到打印机中。每台计算机可以有若干传真卡,但是只应该有一个软件负责管理传真卡,以避免出现两份传真作业同时传到传真卡中的情况。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。
  需要管理的资源包括软件内部资源,譬如,大多数的软件都有一个(甚至多个)属性(properties)文件存放系统配置。这样的系统应当由一个对象来管理一个属性文件。
需要管理的软件内部资源也包括譬如负责记录网站来访人数的部件,记录软件系统内部事件、出错信息的部件,或是对系统的表现进行检查的部件等。这些部件都必须集中管理,不可整出多头。
  这些资源管理器构件必须只有一个实例,这是其一;它们必须自行初始化,这是其二;允许整个系统访问自己这是其三。因此,它们都满足单例模式的条件,是单例模式的应用。
三、单例模式的特征
  3.1只能产生一个对象的实例。
    通过构造器私有化来限制外部创建该对象实例。
  3.2在类中定义静态的,私有对象。
    private static ...
  3.3提供静态共有的函数(方法)用于创建返回这个静态私有唯一对象给外部访问。
    public static ...

四、单例模式的优点:
  4.1.由于单例模式只能生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其它依赖对象时,则可以通过在应用启动时产生一个单例对象,然后永久驻留内存的方式来解决。
  4.2单例模式可以在系统设置全局访问点,优化共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。

五、单例模式的创建方式及其示例
  单例模式分为饿汉式、懒汉式两种。
  5.1饿汉式(指全局的单例实例在类装载时创建)
  
package edu.aeon.model.singleton;
/**
 * 单例模式:饿汉式(类装载时创建)
 * 步骤:
 *     1.构造器私有化(防止外部new)
 *     2.提供静态的、私有的代表该对象的实例
 *     3.提供静态的、公开的函数(方法)供外部访问应用该模式唯一实例
 * @author aeon
 */
public class HungrySingleton {
    //1.构造器私有化(防止外部new)
    private HungrySingleton(){};
    //2.提供静态的、私有的代表该对象的实例
    private static HungrySingleton hungrySingletonInstance=new HungrySingleton();
    //3.提供静态的、公开的函数(方法)供外部访问应用该模式唯一实例
    public static HungrySingleton getSinletonInstance(){
        return hungrySingletonInstance;
    }
}
  5.2懒汉式(调用时创建)
    
package edu.aeon.model.singleton;
/**
 * 单例模式:懒汉式(类装载及创建)
 * 步骤:
 *     1.构造器私有化(防止外部new)
 *     2.提供静态的、私有的代表该对象的实例的空引用
 *     3.提供静态的、公开的函数(方法)供外部访问
 * @author aeon
 */
public class LazySingleton {
    //1.构造器私有化(防止外部new)
    private LazySingleton(){}
    //2.提供静态的、私有的代表该对象的实例的空引用(调用时才给值)
    private static LazySingleton lazySingletonInstance=null;
    //3.提供静态的、公开的函数(方法)供外部访问应用该模式唯一实例
    public static LazySingleton getLazySingletonInstance(){
        if(null==lazySingletonInstance){
            lazySingletonInstance=new LazySingleton();
        }
        return lazySingletonInstance;
    }
}

两方方式的区别: 

  饿汉式:天然线程安全、调用效率高、不能延时加载、资源利用率低(有可能永远不会利用这资源)。

  懒汉式:线程安全、调用效率低、可延时加载、资源利用效率高。

六、单例模式之懒汉式进化(主要适应高并发引发的线程安全问题)

  6.1排队、加锁(synchronized)

package edu.aeon.model.singleton;
/**
 * 单例模式:懒汉式(类装载及创建)
 * 步骤:
 *     1.构造器私有化(防止外部new)
 *     2.提供静态的、私有的代表该对象的实例的空引用
 *     3.提供静态的、公开的函数(方法)供外部访问
 * @author aeon
 */
public class LazySingleton {
    //1.构造器私有化(防止外部new)
    private LazySingleton(){}
    //2.提供静态的、私有的代表该对象的实例的空引用(调用时才给值)
    private static LazySingleton lazySingletonInstance=null;
    //3.提供静态的、公开的函数(方法)供外部访问应用该模式唯一实例
    public static synchronized LazySingleton getLazySingletonInstance(){
        if(null==lazySingletonInstance){
            lazySingletonInstance=new LazySingleton();
        }
        return lazySingletonInstance;
    }
}

  由于6.1使用了方法上的加锁、多线程访问时都要在该方法上进行排队等待、所以效率非常低。

  6.2双重检查锁  

package edu.aeon.model;

/**
 * [说明]:单例模式_双重检查锁(不建议使用)
 * 步骤:
 *     1.构造私有化
 *     2.提供静态私有的单例实例属性
 *     3.提供静态公开的函数(方法)返回这个实例供外部访问
 * @author aeon
 *
 */
public class SingletonDCL {
    //1.构造私有化
    private SingletonDCL() {}
    //2.提供静态私有的单例实例属性
    private static SingletonDCL singletonDCL = null;
    //3.提供静态公开的函数(方法)返回这个实例供外部访问
    public static SingletonDCL getInstance() {
        if (singletonDCL == null) {
            SingletonDCL sdcl;
            synchronized (SingletonDCL.class) {
                sdcl = singletonDCL;
                if (sdcl == null) {
                    synchronized (SingletonDCL.class) {
                        if (sdcl == null) {
                            sdcl = new SingletonDCL();
                        }
                    }
                    singletonDCL = sdcl;
                }
            }
        }
        return singletonDCL;
    }
}

  由于6.2的双重检查锁模式将同步内容下方到if内部,提高了执行的效率,不必每次获取对象时都进行同步,只有第一次才同步、创建了以后就没必要了,因为创建之后就不会进入if条件了、直接返回该唯一实例。

  缺点:由于编译器优化原因和JVM底层内部模型原因,偶尔会出现问题,所以不建议使用。

  6.3静态内部类(推荐)  

package edu.aeon.model.singleton;
/**
 * 单例模式:懒汉式(类装载及创建)进化(主要适应高并发下引发的线程安全问题)
 * 步骤:
 *     1.构造器私有化(防止外部new)
 *     2.提供静态内部类创建实例(JVM自动加锁、线程安全、只加载一次)
 *     3.提供静态的、公开的函数(方法)供外部访问
 * @author aeon
 */
public class LazySingleton {
    //1.构造器私有化(防止外部new)
    private LazySingleton(){}
    //2.提供静态内部类创建实例(JVM自动加锁、线程安全、只加载一次)
    private static class temp{
        private static final LazySingleton lazySingletonInstance=new LazySingleton();
    }
    //3.提供静态的、公开的函数(方法)供外部访问应用该模式唯一实例
    public static synchronized LazySingleton getLazySingletonInstance(){
        return temp.lazySingletonInstance;
    }
}

  外部类没有static属性,则不会像饿汉式那样立即加载对象。

  只有真正调用时,才会加载静态内部类。加载类时是线程安全的。该实例是static final类型的,从而保证了内存中只有这样一个实例的存在,而且只能被赋值一次,从而保证了线程安全性。
  兼备了并发高效调用和延迟加载的优势,可谓单例模式中的"高富帅",集各种优势与一身啊、所以静态内部类实现的单例模式是特别推荐使用的。

   6.4使用枚举实现单例  

package edu.aeon.model;
/**
 * [说明]:枚举实现单例模式(枚举本身就是单例)
 * @author aeon
 */
public enum SingletonEnum {
    singletonEnumInstance;
    /**
     * 实现一些其它的操作
     */
    public void otherOperator(){
        //...
    }
}

  优点:

    实现简单
    枚举本身就是单例模式。由JVM从根本上提供保障避免了通过反射和反序列化的漏洞!
  缺点

    唯一不足就是没有延迟加载。

  

如有任何疑问可联系邮箱: 给我发邮件、或直接联系QQ:1584875179 || 点返回首页

原文地址:https://www.cnblogs.com/aeon/p/10204154.html