`

创建型-单例模式

 
阅读更多

单例模式

定义

在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。

要点有三个;

一是某个类只能有一个实例;

二是它必须自行创建这个实例;

三是它必须自行向整个系统提供这个实例。

 

应用与实例

单例模式的写法其实有很多种,饿汉式、懒汉式、线程安全式等等。上网一搜都根据性能等问题写出了各种写法。对单例模式的要求一般也就如下一些要求。

1.最基本要求:每次从getInstance()都能返回一个且唯一的一个对象。

2.稍微高一点的要求:这个方法能适应多线程并发访问。

3.再提高一点的要求:性能尽可能高。

4.再有一点:实现懒加载(Lazy Load),在需要的时候才被构造。

 

一般Singleton模式通常有三种形式:

第一种形式:懒汉式,也是常用的形式。

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

 

第二种形式:饿汉式

publicstaticclassSingleton{ 

//在自己内部定义自己的一个实例,只供内部调用
private static final Singletoninstance=newSingleton();
private Singleton(){
     //dosomething
}
//这里提供了一个供外部访问本class的静态方法,可以直接访问
public static SingletongetInstance(){
    returninstance;
}

}

 

 

第三种形式: 双重锁的形式。

public static class Singleton{

private static Singleton instance=null;

private Singleton(){
     //dosomething
}

public static Singleton getInstance(){
  if(instance==null){
      synchronized(Singleton.class){
         if(null==instance){
          instance=newSingleton();
        }
    }
 }
returninstance;
}

}

 /这个模式将同步内容下方到if内部,提高了执行的效率,不必每次获取对象时都进行同步,只有第一次才同步,创建了以后就没必要了。

 

              

   饿汉式单例在自己被加载时就将自己实例化,如果从资源利用效率角度来讲,比懒汉式单例类稍差些。但是从速度和反应时间角度来讲,则比懒汉式要稍好些。                  

但是遗憾的是:懒汉式单例类也不能被继承。          

       我们克服前两种单例类不能被继承的缺点,我们可以使用另外一种特殊化的单例模式,它被称为单例注册表。               

  

   Public class RegSingleton{              
   Static private HashMap registry=new HashMap();              
   //静态块,在类被加载时自动执行                
  Static{                  
       RegSingleton rs=new RegSingleton();                
       Registry.put(rs.getClass().getName(),rs);                
  }                
  //受保护的默认构造函数,如果为继承关系,则可以调用,克服了单例类不能为继承的缺点                 
 Protected RegSingleton(){}
            
//静态工厂方法,返回此类的唯一实例          
 public static RegSingleton getInstance(String name){    
       if(name==null){          
              name=” RegSingleton”;         
        }              
       if(registry.get(name)==null){      
            try{                   
                 registry.put(name,Class.forName(name).newInstance());    
             } Catch(Exception ex){
                 ex.printStackTrace();
             }        
         }             
      Return (RegSingleton)registry.get(name); 
}  
  
}     

            

 下面我们来看看Spring中的单例实现,当我们试图从Spring容器中取得某个类的实例时,默认情况下,Spring会才用单例模式进行创建。                  

<bean id="date" class="java.util.Date"/>          
<bean id="date" class="java.util.Date" scope="singleton"/>   (仅为Spring2.0支持)           <bean id="date" class="java.util.Date" singleton="true"/>          

     

   以上三种创建对象的方式是完全相同的,容器都会向客户返回Date类的单例引用。那么如果我不想使用默认的单例模式,每次请求我都希望获得一个新的对象怎么办呢?很简单,将scope属性值设置为prototype(原型)就可以了            

     <bean id="date" class="java.util.Date" scope="prototype"/>            

     通过以上配置信息,Spring就会每次给客户端返回一个新的对象实例。            

     那么Spring对单例的底层实现,到底是饿汉式单例还是懒汉式单例呢?都不是。Spring框架对单例的支持是采用单例注册表的方式进行实现的,源码如下:          

  

public abstract class AbstractBeanFactory implements              
   ConfigurableBeanFactory{              
   /**                  
    * 充当了Bean实例的缓存,实现方式和单例注册表相同    
    */              
   private final Map singletonCache=new HashMap();   
                           
   public Object getBean(String name)throws BeansException{              
        return getBean(name,null,null);              
    }            
     ...          
   public Object getBean(String name,Class requiredType,Object[]args)throws BeansException    //对传入的Bean name稍做处理,防止传入的Bean name名有非法字符(或则做转码)              
   String beanName=transformedBeanName(name);                
   Object bean=null;                                
   //手工检测单例注册表                
   Object sharedInstance=null;                  
   //使用了代码锁定同步块,原理和同步方法相似,但是这种写法效率更高 、
    synchronized(this.singletonCache){          
       sharedInstance=this.singletonCache.get(beanName);          
    }  
    if(sharedInstance!=null){      
           ...              
         //返回合适的缓存Bean实例            
          bean=getObjectForSharedInstance(name,sharedInstance);              
    }else{              
         ...                
         //取得Bean的定义      
         RootBeanDefinition mergedBeanDefinition=getMergedBeanDefinition(beanName,false);             ...                
        //根据Bean定义判断,此判断依据通常来自于组件配置文件的单例属性开关              
        //<bean id="date" class="java.util.Date" scope="singleton"/>              
        //如果是单例,做如下处理                
        if(mergedBeanDefinition.isSingleton()){              
            synchronized(this.singletonCache){            
            //再次检测单例注册表              
            sharedInstance=this.singletonCache.get(beanName);          
            if(sharedInstance==null){        
                  ...                
                try {    
                  //真正创建Bean实例
                  sharedInstance=createBean(beanName,mergedBeanDefinition,args);          
                  //向单例注册表注册Bean实例            
                 addSingleton(beanName,sharedInstance);
                  } catch (Exception ex) {                      
                         ...
                  }
                  finally{                          
                            ...
                  }              
              }
                 }              
   bean=getObjectForSharedInstance(name,sharedInstance);  
               }            
     //如果是非单例,即prototpye,每次都要新创建一个Bean实例        
     //<bean id="date" class="java.util.Date" scope="prototype"/>              
   else{            
     bean=createBean(beanName,mergedBeanDefinition,args);        
         }          
       }        
         ...            
     return bean;       
           }        
         }     

            

 以上的源码对于很多同学来说,可能感觉比较恐怖,但是学习要学会抓住要领,刚才的源码中,大家真正要记住的是Spring对bean实例的创建是采用单例注册表的方式进行实现的,而这个注册表的缓存是HashMap对象,如果配置文件中的配置信息不要求使用单例,Spring会采用新建实例的方式返回对象实例

 

 优点:

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

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

 

缺点:

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

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

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

 

 

分享到:
评论

相关推荐

    JAVA-设计模式-创建型模式-单例模式

    JAVA-设计模式-创建型模式-单例模式

    c++设计模式-单例模式

    c++设计模式-创建型模式-单例模式源码,懒汉式 饿汉式源码,qt工程,单例模式线程安全问题,单例模式实现方式

    设计模式_创建型_单例模式.md

    设计模式_创建型_单例模式

    最新设计模式超级详解+Tomcat架构源码分析+Spring源码分析 资深级设计模型课程

    ├─day01-总览设计模式-04-创建型模式-单例的应用场景.mp4 ├─day01-总览设计模式-05-创建型模式-原型模式.mp4 ├─day01-总览设计模式-06-创建型模式-工厂-简单工厂模式.mp4 ├─day01-总览设计模式-07-创建型...

    小D深入浅出设计模式+框架源码剖析实战

    ├─第三章 创建型设计模式-单例设计模式和应用 │ 3.1江湖传言里的设计模式-单例设计模式.mp4 │ 3.2代码实战-单例设计模式中的懒汉实现方式.mp4 │ 3.4单例模式中的饿汉实现和选择问题.mp4 │ 3.5JDK源码里面...

    设计模式作业+课设答辩报告+课程ppt

    part3-创建型-单例模式.ppt part4-创建型-工厂模式.ppt part5-创建型-生成器模式.ppt part6-结构型设计模式-适配器模式.ppt part8-结构型设计模式-代理模式.pptx part9-结构型设计模式-外观模式.pptx part1O-补充...

    创建型模式之单例模式(Singleton Pattern)

    4、单例模式(Singleton Pattern) 用意:仅允许生成一个对象时

    单例模式 Singleton Pattern

    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个...

    Java单例模式应用研究.pdf

    单例模式是一种最简单的创建型设计模式,主要用于对系统资源的管理与控制,在软件开发中大量使用,如Windows的文件系统、回收站、打印机等。文中通过内容管理系统项目,深入剖析了几种常用的单例类,包括饿汉式单例类、...

    Java单例模式设计

    代码中演示了Java设计模式中的单例模式,其中包括饿汉单例模式,懒汉单例模式以及序列化饭序列化单例模式。在实际的开发中,可以直接借鉴使用。

    【设计模式】(四)–创建型模式–单例模式

    【设计模式】(四)–创建型模式–单例模式单例模式的定义饿汉式单例模式懒汉式单例模式饿汉式与懒汉式的区别:单例模式的优点单例模式的缺点Java中其他生成单例的方式使用Spring框架,Spring框架默认就是单例双重...

    java单例模式看这一篇就够了

    单例模式属于创建型模式 单例模式的常见写法 一、饿汉式单例 顾名思义饿汉式单例是在类加载的时候就立即初始化,并且创建单例对象。绝对线程安全,在线程还没出现以前就被实例化了,不可能存在访问安全

    JS 设计模式之:单例模式定义与实现方法浅析

    单例模式(Singleton)属于创建型的设计模式,它限制我们只能创建单一对象或者某个类的单一实例。 通常情况下,使用该模式是为了控制整个应用程序的状态。在日常的开发中,我们遇到的单例模式可能有:Vuex 中的 ...

    [创建型模式] 单例模式(Singleton Pattern)

    一个类可以创建多个对象,这是面向对象的语言特性,想要实现单例模式,就要屏蔽这个特性,防止系统可以随意创建类的对象。 要做到这一点,通常做法就是利用private关键字将类的构造方法私有化,使外部调用者无法利用...

    Singleton(单例模式)

    在Java应用中,单例对象能保证在一个...3、有些像交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了,只有使用单例模式,才能保证核心交易服务器独立控制整个流程。 CSDN代码的详细解释。

    [创建型模式] 单例模式的理解

    NULL 博文链接:https://jacky-dai.iteye.com/blog/2297405

    【JavaScript源代码】JS实现单例模式的6种方案汇总.docx

     今天在复习设计模式中的-创建型模式,发现JS实现单例模式的方案有很多种,稍加总结了一下,列出了如下的6种方式与大家分享 大体上将内容分为了ES5(Function)与ES6(Class)实现两种部分 单例模式就是在系统中...

Global site tag (gtag.js) - Google Analytics