背景:
有一些对象只需要一个,比如:线程池、缓存、对话框、处理偏好设置、注册表等等。如果制造出多个实例就会导致许多问题产生,如:程序的行为异常、资源使用过量等。
设计:
私有化构造器同时定义一个类方法去初始化构造器。
实现:
// 最简单的实现
public class Singleton {
private static Singleton uniqueInstance; // 定义一个静态变量用于记录Singleton类的唯一实例
// 私有化构造器
private Singleton(){}
// 创建类方法,方便被调用,被用到时才会被调用,这就是延迟实例化。
public static Singleton getInstance(){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
但可这样可能才某些情况下出现问题,比如多线程中,可能还是会创建多个实例,我们需要保证线程安全。
1.把getInstance()方法改成同步方法,可以轻松解决。
// 增加一个synchronized关键字到getInstance()方法中可以迫使每个线程在进入这个方法之前
// 要先等候其他的线程离开该方法,故不会出现两个线程同时进入该方法。
public static synchronized Singleton getInstance() {
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
适用性:保证可行的最直接方法,同步一个方法性能下降100倍,如果对性能没有要求的话可以直接用,但如果频繁运行该方法,需要重新考虑。
2.使用“急切”创建实例,而不用延迟实例化的做法
// 在静态初始化器中创建单例,保证了线程安全
public class Singeton {
private static Singeton uniqueInstance = new Singleton();
private Singleton(){}
public static Singleton getInstance() [
return uniqueInstance;
}
}
3.用“双重检查加锁”,在getInstance()中减少使用同步
首先检查是都创建实例,如果未创建再进行同步。
public class Singleton {
private volatile static Singleton uniqueInstance; // volatile关键字确保变量被实例化时,多线程能正确处理
private Singleton(){}
public static Singleton getInstance(){
if(uniqueInstance == null) {
synchronized (Singleton.class){
if(uniqueInstance == null){ // 在检查一次
uniqueInstance = new Singleton();
}
}
}
}
}
适用性:该方法不适用于java 4及以前的版本,如果没有性能要求可以考虑,似乎杀鸡用了牛刀
总结:
单例模式:确保一个类只有一个实例,并提供一个全局访问点。
单例模式可以和全局变量一样方便,又没有全局变量的缺点。(全局变量的缺点:程序可能一开始就创建好了对象,但这个对象可能一直没用到,即急切实例化和延迟实例化的区别)
全局变量基本上就是对对象的静态引用,可以提供全局访问,但是不能保证只有一个实例化,我们使用单例模式的目的是确保类只有一个实例并提供全局访问。
此外大量使用全局变量也会变相鼓励开发人员用许多全局变量指向许多小对象来造成命名空间的污染。