设计模式之Singleton


这是一个最简单的模式,但同时也是最容易被误用、滥用的模式,怎样才能用好这个看似简单的模式呢?
     在软件系统中,经常有一些类,必须保证它们在系统中只存在一个实例,才能确保逻辑的正确性以及良好的效率。例如,thread pools,caches,dialog boxes,还有Eclipse里面的preferencs设置。
     下面是一个最简单的版本:
public class Singleton{
    
private static Singleton uniqueInstance;

    
private Singleton(){}

    
public static Singleton getInstance(){
        
if (uniqueInstance == null){
            uniqueInstance 
= new Singleton();
        }

        
return uniqueInstance;
    }

}


这个版本没有考虑到多线程下的执行情况。如果我们有两个线程,都在执行这一段代码,这样就会存在两个实例,怎样避免这个问题?
public class Singleton{
    
private static Singleton uniqueInstance;

    
private Singleton(){}

    
public static synchronized Singleton getInstance(){
        
if (uniqueInstance == null){
            uniqueInstance 
= new Singleton();
        }

        
return uniqueInstance;
    }

}

我们可以给这个方法加一个同步,这样就保证了不会有两个线程同时进入这个方法,但是同步的代价太大了。
每个线程也只有在第一次进入这个方法的时候有必要进行同步,当获得了一个实例之后,同步就是多余的了。
还好,我们可以利用JVM的特性,看下一段代码:
public class Singleton{
    
private static Singleton uniqueInstance = new Singleton();

    
private Singleton(){}

    
public static Singleton getInstance(){
        
return uniqueInstance;
    }

}


JVM已经在线程访问这个方法之前把静态字段初始化了,因此,在你的线程执行getInstance的时候,肯定会返回唯一的一个实例。同样的,.Net的CLR也支持同样的功能,不过,它需要的是一个静态只读字段。
或者,我么可以使用双检查:
public class Singleton{
    
private volatile static Singleton uniqueInstance;

    
private Singleton(){}

    
public static synchronized Singleton getInstance(){
        
if (uniqueInstance == null){
            
synchronized (Singleton.class){
                
if (uniqueInstance == null){
                    uniqueInstance 
= new Singleton();
                }

            }

        }

        
return uniqueInstance;
    }

}
这样,同步的代价变小了,而且volatile关键字可以保证多线程下实例访问的正确性,并且它的同步代价是很低的。

以上记述了几种单例模式的基本写法,在实际的项目中怎样用,肯定不会是这么简单的,如果诸位有什么心得,还请多多指教啊。
posted on 2007-10-02 09:35  Game_over  阅读(381)  评论(0)    收藏  举报