创建模式之Singleton——单例模式

1、简介
        作为对象的创建模式,单例模式确保某个类只有一个实例,而且自行实例化,并向系统提供这个实例,这个类称为单例类。他有三个要点:
        只能有一个实例
        自行创建这个实例
        自行向系统提供这个实例

2、使用场景及能解决的问题
        当你只需要一个类实例时,Singleton才真正有用;如果类拥有几个实例,使用Singleton就不再适用。

    设计系统时,通常希望控制对象的用法,防止用户复制对象或建立新实例。例如,你可以使用它创建一个连接池,每次程序需要往数据库中写入内容时才创建一个新连接的做法并不明智;相反,一个或一组已经在池中的连接就可以使用Singleton模式实例化。

        Singleton模式常常和工厂方法模式一同使用,创建一个系统级资源,使用这个资源的代码并不知道它的特殊类型。抽象窗口工具包(AWT)就是组合使用这两个模式的典型例子。在GUI应用程序中,对每个应用程序实例,你一般只需要一个图形元素的实例,如打印(Print)对话框或OK按钮。

3、类图


4、单例模式的运行机制
        Singleton是一个无法实例化的对象。这种设计模式暗示,在任何时候,只能由JVM创建一个Singleton
(对象)实例。如果实例不存在,你通过创建类的新实例的方法建立一个类来执行这个模式;如果存在类的一个实例,就只会返回那个对象的一个引用。
    下面看看单例模式的几种实现方式:
   
 
    方式1:
    

public class Singleton {

  
//注意构造方法必须是私有的

      private Singleton(){}


  
//在自己内部定义自己一个实例,是不是很奇怪?
  
//注意这是private 只供内部调用


  private static Singleton instance = new Singleton();

  
//这里提供了一个供外部访问本class的静态方法,可以直接访问  

  public static Singleton getInstance() 
{
    
return instance;   
   }
 
}
 


    方式2:

public class Singleton 
  
private static Singleton instance = null;

  
public static synchronized Singleton getInstance() 
{

      
//这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     
      
//使用时生成实例!

      if (instance==null)
        instance=
new Singleton();
            
return
 instance;
      }
 
       }





    方式2就是我们说的:滞后初始化(Lazy Initialization)。为什么会有滞后初始化这种实现方式出现呢?我们可用看到在第一种实现方式中无法向单例模式的构造方法传递参数,而使用滞后初始化的方式,我们可用在调用getInstance()方法的时候向方法中传递参数。

凡事有好处必然有坏处,滞后初始化的一个弊病就是在多线程或分布式的环境下有可能出现混乱:

    “有时在某些情况下,使用Singleton并不能达到Singleton的目的,如有多个Singleton对象同时被不同的类装入器装载;在EJB这样的分布式系统中使用也要注意这种情况,因为EJB是跨服务器,跨JVM的。” --摘自www.jdon.com-《GoF 23种设计模式解析》

    “在多线程环境下,我们无法保证一个方法能够持续运行到结束,其他线程的方法才开始运行。因而可能存在这样一种情形:两个线程几乎同时尝试初始化单例类。假设第一个方法发现单例为空,而第二个方法在此刻开始运行,它也会发现该单例为空。接下来,这两个方法都将对该单例进行初始化。”  --摘自《Java设计模式》

    
那么在多线程的环境下我们怎么更安全的使用单例模式呢?
  
  
    方式3:
   
 《Java并发编程》一书建议使用属于当前类的锁进行同步,代码如下:

public class Singleton 

    
private static Singleton instance = null
;
   
//注意这里的static非常重要,如果为对象变量则因为存在多份拷贝而起不到限制的作用

    private static Object classLock = Singleton.class
     
public static Singleton getInstance() 
{
      
//这个方法比上面有所改进,不用每次都进行生成对象,只是第一次使用时生成实例

          synchronized(classLock){
              
if (instance==null
)
          instance=
new
 Singleton();
              
return
 instance;
          }
 
          }
      
     }



    在第一个线程开始滞后初始化的时候,如果有另一线程也准备开始初始化,这时候,第二个线程将停止执行,等待获取对象classLock的锁。当第二个线程获取这个锁并开始执行初始化的时候,它会发现该单例已不再为空(因为只存在该类的唯有实例,我们可以使用单个静态锁)。
   
 
    或者:

public class Singleton
{
   
private Singleton() {}
  
   
private static class
 SingletonHolder
   
{
    
private final static Singleton INSTANCE = new
 Singleton();
   }
  
   
public static
 Singleton getInstance()
   
{
    
return
 SingletonHolder.INSTANCE;
   }

}

    另一个解决办法是在getInstance()方法声明中添加synchronized关键字: 

public static synchronized Singleton getInstance()

5、使用注意事项
        单例模式类不能实现Clonable接口,以防被克隆而产生多个实例
    
 public Object clone() throws 

CloneNotSupportedException 
{

         
throw new
 CloneNotSupportedException();

}



        单例模式类不能实现Serializable接口,以防被序列化而产生多个实例

        根据不同的执行,你的Singleton类和它的所有数据可能被当作垃圾收集。因此,在应用程序运行时,你必须保证存在一个Singleton类的实时引用。

        

posted on 2008-05-28 16:57 云淡风清 阅读(734) 评论(0)  编辑  收藏 所属分类: Design Patterns


只有注册用户登录后才能发表评论。


网站导航:
 
<2008年5月>
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

导航

统计

常用链接

留言簿(1)

随笔分类(15)

随笔档案(15)

收藏夹(1)

搜索

最新评论

阅读排行榜

评论排行榜