分类: Java
2013-02-26 23:47:11
在某些应用的场合,一个或者某些类只需要或者只能存在一个实例,这种情况下单例模式便派上了用场。在windows操作系统中,垃圾回收站就是典型的应用,还有很多工程中的配置文件等等,系统中只需要一个实例。当系统需要该类实例时,如果系统中存在该类的实例直接返回实例。如果系统没有该类实例时,创建一个新的类实例,从而保证系统中只存在该对象的一个实例。
系统中类实例创建主要由于其构造方法来完成的,因此在这个时候就需要控制类的初始化过程。下面有两个例子,用不同方式产生所需类的单一实例。
1: 饿汉式
2: public class EagerSingletonPattern {
3:
4: private static final EagerSingletonPattern ins = new EagerSingletonPattern();
5:
6: private EagerSingletonPattern(){
7: }
8:
9: public static EagerSingletonPattern getInstance(){
10: return ins;
11: }
12: }
下面便是懒汉式:
1: 懒汉式
2: public class LazySingletonPattern {
3: private static LazySingletonPattern ins = null;
4:
5: private LazySingletonPattern(){
6: }
7:
8: public synchronized static LazySingletonPattern getInstance(){
9: if( ins == null){
10: ins = new LazySingletonPattern();
11: }
12: return ins;
13: }
14: }
饿汉式在类加载的时候便初始化该类的一个实例,系统需要该类实例时直接返回。在系统调用时响应速度较快,但系统资源利用率较低。懒汉式在需要类实例的时候,先检查系统中是否已经存在类实例。如果存在直接返回,反之创建一个实例。系统资源利用率较高,但在系统调用时响应速度较饿汉式要慢,而且在多线程中,需要线程之间的同步,有可能在资源初始化的时候耗时比较长。
这2种实现方式基本上可以实现所需的要求。但是在懒汉式实现中,后续线程需要等待执行方法线程完成之后才能进入执行,无疑,这延长了系统响应时间。在《Head First 设计模式》书中提到了对懒汉式的一种改进,我个人认为这种方法是正确的,尽管在《Java与模式》书中提到双重检查成例的不正确性。下面我给出《Head First 设计模式》中双重检查加锁的例子。
1: public class DCLSingletonPattern {
2:
3: private volatile static DCLSingletonPattern ins= null;
4:
5: private DCLSingletonPattern(){}
6:
7: public static DCLSingletonPattern getInstance(){
8: if(ins == null){ //第一重检查
9: synchronized(DCLSingletonPattern.class){//线程同步
10: if(ins == null){ //第二重检查
11: ins = new DCLSingletonPattern();
12: }
13: }
14: }
15: return ins;
16: }
17: }
volatile关键词确保变量ins在初始化为实例后,多线程能正确的处理ins变量。
上面给出的例子都或多或少都存在一些缺陷,它们的通病都在于其不能被子类所继承。如果能被子类所继承的话,其构造方法至少为protect,而不能为private。但是如果修改成protect,该类就失去了单例模式的特性--其他类可以直接调用其构造方法产生新的实例。因此,在《Java与模式》中给出了登记式的实现方法。但是子类构造方法也不能为private,其构造方法对外开放或部分开放,只能按照登记式实例化可以产生单例,因而也并不完美。在此,我就不给这个例子了,实际应用中,懒汉式和饿汉式基本上够用。