有一些对象我们只需要一个(也只能有一个)比如:线程池、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变量。
这个方法在对性能要求高的时候可以用,不然就是杀鸡用牛刀了,呵呵。