现在的位置: 首页 > 综合 > 正文

设计模式学习之–Singleton(单例)模式

2013年08月15日 ⁄ 综合 ⁄ 共 5252字 ⁄ 字号 评论关闭

首先我们先看看一个例子,在说什么是单利模式(Singleton):

Java代码  收藏代码
  1. package
     org.bestupon.dp.singleton;  
  2. /**
     

  3.  
  4. * @author BestUpon
     
  5. * @email bestupon@foxmail.com
     
  6. * @date 2010-6-13上午11:08:28
     
  7. * @ask jdk中Runtime这个类似就是一个单例模式的应用:
     

  8.  
  9. * @answer
     
  10. */
      
  11. public
     
    class
     Test4RunTime {  
  12.   
  13. public
     
    static
     
    void
     main(String[] args) {  
  14. Runtime runtime = Runtime.getRuntime();  
  15. runtime.freeMemory();  
  16. }  
  17. }  

上面的例子可以看出在没有使用new,却获得了一个Runtime对象,这是为什么呢?让我们看看java.lang.Runtime.java的源码到底是怎么一回事:

Java代码  收藏代码
  1. public
     
    class
     Runtime {   
  2.     private
     
    static
     Runtime currentRuntime = 
    new
     Runtime();   
  3.     public
     
    static
     Runtime getRuntime() {   
  4.         return
     currentRuntime;   
  5.     }   
  6.    /** Don't let anyone else instantiate this class */
       
  7.    private
     Runtime() {}   
  8.     // 以下略 
      
  9. }  

 以上是Runtime.java开头部分的代码,我们可以很清楚的看见,一开头就直接

new Runtime(), 一个对象,并且是静态的。在getRuntime()的时候,

直接将其返回给请求的客户端。


 

上面结构即采用
Singleton

模式设计,其结构使用
UML 描述

如下所示:


Singleton



的英文意义是独身,也就是只有一个人,应用在面向对象语言上,通常翻译作单例:单一个实例(


Instance



)。


Singleton



模式可以保证一个类别






只有一个实例,并只提供一个访问(


visit



)这个实例的方法。



1.





定义:




单例模式就是确保一个类中只有一个实例,并且该实例必须自动创建,并向整个系统提供该实例。

2.





使用时机:

当系统要求一个类只有一个实例时,就需要使用用单例模式。


 

有几个实例上面结构的方法,可以在第一次需要实例时再建立对象,也就是采用所谓的

Lazy Initialization


 

Java代码  收藏代码
  1. public
     
    class
     Singleton {   
  2.     private
     
    static
     Singleton instance = 
    null
    ;   
  3.     private
     Singleton() {   
  4.         // .... 
      
  5.     }   
  6.     public
     
    static
     Singleton getInstance() {   
  7.         if
     (instance == 
    null
    ) {  
  8.             instance = new
     Singleton();   
  9.         }  
  10.         return
     instance;   
  11.     }   
  12.   
  13.     // .. 其它实例 
      
  14. }  

 
上面的实例适用于单线程的程序,在多线程的程序下,以下的写法在多个线程的竞争资源下,将仍有可能产生两个以上的实例,例如下面的情况:

 

Java代码  收藏代码
  1. Thread1: 
    if
    (instance == 
    null

    // true
      
  2. Thread2: if
    (instance == 
    null

    // true
      
  3.   
  4. Thread1: instance = new
     Singleton(); 
    // 产生一个实例
      
  5. Thread2: instance = new
     Singleton(); 
    // 又产生一个实例
      
  6.   
  7. Thread1: return
     instance; 
    // 回传一个实例
      
  8. Thread2: return
     instance; 
    // 又回传一个实例
      

 
在多线程的环境下,为了避免资源同时竞争而导致如上产生多个实例的情况,加上同步(

synchronized

)机制:

 

Java代码  收藏代码
  1. public
     
    class
     Singleton {  
  2.       private
     
    static
     Singleton instance = 
    null
    ;  
  3.       private
     Singleton(){}  
  4.       synchronized
     
    static
     
    public
     Singleton getInstance() {  
  5.           if
     (instance == 
    null
    ) {  
  6.               instance = new
     Singleton();  
  7.           }  
  8.           return
     instance;  
  9.       }  
  10.   }  

 
不过这种简单的写法不适合用于像服务器这种服务很多线程的程序上,同步机制会造成相当的效能低落,为了顾及

Singleton



Lazy
Initialization


与效能问题,因而有了

Double-check
Locking


的模式:

 

Java代码  收藏代码
  1. public
     
    class
     Singleton {  
  2.       private
     
    static
     Singleton instance = 
    null
    ;  
  3.       private
     Singleton(){}  
  4.       public
     
    static
     Singleton getInstance() {  
  5.           if
     (instance == 
    null
    ){  
  6.               synchronized
    (Singleton.
    class
    ){  
  7.                   if
    (instance == 
    null
    ) {  
  8.                        instance = new
     Singleton();  
  9.                   }  
  10.               }  
  11.           }  
  12.           return
     instance;  
  13.       }  
  14.   }  

 
也就是只有在第一次建立实例时才会进入同步区,之后由于实例已建立,也就不用进入同步区进行锁定。

Java



Runtime

类别的作法简单的多,



它舍弃了

Lazy Initialization

,如果您要取得单例的机会不是很多,可以用这种方式:

 

Java代码  收藏代码
  1. public
     
    class
     Singleton {   
  2.     private
     
    static
     Singleton instance = 
    new
     Singleton();   
  3.     private
     Singleton() {   
  4.         // .... 
      
  5.     }   
  6.     public
     
    static
     Singleton getInstance() {   
  7.         return
     instance;   
  8.     }   
  9.     // 其它实例 
      
  10. }  

 
Singleton

本身的观念简单但应用



很广,因而很多时候必须对实际环境作一些考虑与调整。

3.总结:

单例模式可以分为两种:饿汉式和懒汉式两种,饿汉是在系统启动的一开始就初始化好了实例,而懒汉式是在第一次访问的时候才初始化实例。

 

Java代码  收藏代码
  1. package
     org.bestupon.dp.singleton;  
  2.   
  3. /**
     
  4.  * @author BestUpon
     
  5.  * @email bestupon@foxmail.com
     
  6.  * @date 2010-6-13上午11:34:27
     
  7.  * @ask 饿汉式单利模式
     
  8.  * @answer
     
  9.  */
      
  10. public
     
    class
     HungerSingleton {  
  11.   
  12.     /**
     
  13.      * 一开始就初始化好了实例
     
  14.      */
      
  15.     private
     
    static
     HungerSingleton instance = 
    new
     HungerSingleton();  
  16.   
  17.     private
     HungerSingleton() {  
  18.     }  
  19.   
  20.     public
     
    static
     HungerSingleton getInstance() {  
  21.         return
     instance;  
  22.     }  
  23.   
  24. }  

 package org.bestupon.dp.singleton;

Java代码  收藏代码
  1. /**
     
  2.  * 
     
  3.  * @author BestUpon
     
  4.  * @email bestupon@foxmail.com
     
  5.  * @date 2010-6-13上午11:41:22
     
  6.  * @ask 懒汉式单例模式
     
  7.  * @answer
     
  8.  */
      
  9. public
     
    class
     LazySingleton {  
  10.     private
     
    static
     LazySingleton instance = 
    null
    ;  
  11.   
  12.     private
     LazySingleton() {  
  13.     }  
  14.   
  15.     public
     
    static
     LazySingleton getInstance() {  
  16.         if
    (instance == 
    null
    ){  
  17.             instance = new
     LazySingleton();  
  18.         }  
  19.         return
     instance;  
  20.     }  
  21. }  

4.优点:

在单利模式中,客户调用类的实例时,只能调用一个公共的接口,这就为整个开发团队提供了共享的概念,

5.缺点:

单利模式在实例化后,是不允许类的继承的;在分布式系统中,当系统的单利模式类被复制运行在多个虚拟机下时,在每一个虚拟机下都会创建一个实例对象,此时如果想知道具体哪个虚拟机下运行着单例对象是很困难的,而且单例类是很难实现序列化的。

 

 

 

由于端午节放假的缘故,各位同仁在期间提出了很多的问题,也指出了我思维的局限性,没有及时的更新于回复各位好友的,深表遗憾!


针对单例模式的很多用法,前面一直是在所单一线程的问题,本来设计多线程的问题就很少,也对双重锁定这个问题没有深入的五挖掘,导致了犯了今天这样的错误。
之后参考了一些资料,针对各位朋友提出的问题与我自身存在的问题,进行改进!
参考的文章是:IBMDeveloperWorks(中国)的网站上的文章


双重检查锁定及单例模式





http://www.ibm.com/developerworks/cn/java/j-dcl.html
)。这篇文章真针对各种问题都有分析,包括可见性等问题。得出了一个结论:双重锁定失效的主要原因是:不同JVM之间的无序写入问题,多线程之间的独占、休眠(记忆复苏)所引起的不同
不问题。

最终本文提出了一个建议:



议不要使用“双重锁定”!



一个解决单例模式的方案:

底线就是:无论以何

种形式,都不应使用双重检查锁定,因为您不能保证它在任何 JVM 实现上都能顺利运行。JSR-133 是有关内存模型寻址问题的,尽管如此,新的内存模型也不会支持双重检查锁定。因此,您有两种选择:

  • 接受如清单 2 中所示的 

    getInstance()


     方法的同步。

  • 放弃同步,而使用一个 

    static


     字段。

选择项 2 如清单 10 中所示:

 

Java代码  收藏代码
  1. /**
     
  2. *<span style="font-
    family: verdana, nsimsun, sans-serif; white-space: normal; line-
    height: 19px;"><a style="color: #5c81a7;" name="code10"><
    strong>使用 static 字段的单例实现</strong></a></span>

     
  3. **/
      
  4. class
     Singleton  
  5. {  
  6.   private
     Vector v;  
  7.   private
     
    boolean
     inUse;  
  8.   private
     
    static
     Singleton instance = 
    new
     Singleton();  
  9.   
  10.   private
     Singleton()  
  11.   {  
  12.     v = new
     Vector();  
  13.     inUse = true
    ;  
  14.     //...
      
  15.   }  
  16.   
  17.   public
     
    static
     Singleton getInstance()  
  18.   {  
  19.     return
     instance;  
  20.   }  

转自JAVAEYE
谢谢

抱歉!评论已关闭.