天天看点

设计模式之单件模式

有一些对象我们只需要一个(也只能有一个)比如:线程池、cache、对话框、处理偏好设置和注册表的对象、日志对象、充当打印机、显卡等设备的驱动程序的对象。

利用静态类变量、静态方法和适当的访问修饰符,就可以做到只存在一个实例。

这是一个经典的单件模式:

public class Singleton {
	private static Singleton uniqueInstance;
	
	private Singleton(){}
	
	public static Singleton getInstance(){
		if(uniqueInstance==null)
			   uniqueInstance=new Singleton();
		
		return uniqueInstance;
	   }//静态方法,引用要使用类名
	

}
           

单件模式确保一个类只有一个实例,并提供一个全局访问点。

它比全局变量多了一个优点:延迟实例化。

一个巧克力工厂具有计算机控制的巧克力锅炉,只能存在一个锅炉,不然会有bad things 发生:

public class ChocolateBoiler {
	private boolean empty;
	private boolean boiled;
	private static ChocolateBoiler uniqueInstance;
	
	private ChocolateBoiler(){
		empty=true;
		boiled=false;
	}
	
	public static ChocolateBoiler getInstance(){
		if(uniqueInstance==null)
			uniqueInstance=new ChocolateBoiler();
		return uniqueInstance;
	}
	
	public void fill(){
		if(isEmpty()){
			empty=false;
			boiled=false;
		}
	}
	
	public void drain(){
		if(!isEmpty() && isBoiled())
			empty=true;
	}
	
	public void boil(){
		if(!isEmpty() && !isBoiled())
			boiled=true;
	}
	
	public boolean isEmpty(){
		return empty;
	}
	
	public boolean isBoiled(){
		return boiled;
	}

}
           

但是,当我们执行以下代码时,就会发生麻烦,竟然允许在加热的过层中加入原料:

ChocolateBoiler boiler=ChocolateBoiler.getInstance();

fill();

boil();

drain();

这里有两个线程要执行这段代码,我们检查getInstance()方法中的操作次序和uniqueInstance的值就会发现,它们重叠了,产生了两个锅炉对象。

处理多线程,只要把getInstance()方法变成同步方法,可以解决:

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

但是同步会降低性能。。。

当getInstance()的性能对应用程序不是很关键,就什么都别做。这是最直接可行的做法!

otherwise,使用急切创建实例,而不用延迟实例化的方法:

public class ChocolateBoiler {
	private boolean empty;
	private boolean boiled;
	private static ChocolateBoiler uniqueInstance=new ChocolateBoiler();
	
	private ChocolateBoiler(){
		empty=true;
		boiled=false;
	}
	
	public static  ChocolateBoiler getInstance(){
		return uniqueInstance;
	}
	//...
}
           

还有一种更好的:双重检查加锁

原理是这样的,首先检查是否实例已经被创建,如果尚未创建,才进行同步。这样一来,只有第一次会同步!!!

public class ChocolateBoiler {
	private boolean empty;
	private boolean boiled;
	private static volatile ChocolateBoiler uniqueInstance;
	
	private ChocolateBoiler(){
		empty=true;
		boiled=false;
	}
	
	public static  ChocolateBoiler getInstance(){
		if(uniqueInstance==null){
			synchronized (ChocolateBoiler.class) {
				if(uniqueInstance==null)
					uniqueInstance=new ChocolateBoiler();		
			}
		}
		return uniqueInstance;
	}
	//...
}
           

其中volatile关键词确保当uniqueInstance变量被初始化成Singleton实例时,多个线程正确处理uniqueInstance变量。

这个方法在对性能要求高的时候可以用,不然就是杀鸡用牛刀了,呵呵。

继续阅读