本文为阅读《Head First 设计模式》一书的摘要总结
单件模式
定义
单件模式 确保一个类只有一个实例,并提供一个全局访问点。
示例
public class Singleton{
private static Singleton uniqueInstance;
private Singletion(){
uniqueInstance = new Singleton();
}
public static Singleton getInstance(){
if (uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
上面的例子中,Singleton
类的构造器的访问修饰符为private
,这意味着我们不能在其他对象中通过new
操作符来实例化这个对象。另外提供了一个静态的getInstance
方法,该方法通过判断静态变量uniqueInstance
是否为空来觉得是否实例化Singleton
对象。我们通过调用getInstance
方法,就可以得到Singleton
单件。
在单线程应用中上面的代码没有问题,但是在多线程应用中,可能就会出现问题了。
在多线程应用中,假设Singleton
还没有创建实例。现在线程1调用getInstance
方法,当通过判断uniqueInstance==null
之后,在实例化对象之前被剥夺了CPU,线程2开始调用getInstance
,并获得了Singleton
实例。当线程2被剥夺CPU之后,线程1继续执行,此时,uniqueInstace
将会引用新的实例。
为了解决这个问题,我们只需要将getInstance
改为同步方法:
public static synchronized Singleton getInstance(){
if (uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
这样,每次调用getInstance
方法,都需要等待其他线程退出该方法之后,当前线程才能进入该方法。但是这便降低了性能。实际是,该方法只有第一次执行时需要同步,一旦设置好uniqueInstace
变量的引用,就不需要同步这个方法了。
- 若是应用程序可以忍受同步带来的效率下降,那么我们就可以不用做任何修改
- 前面的例子采用的是延迟实例化,我们可以采用 预实例化,来消除多线程带来的问题:
public class Singleton{
private static Singleton uniqueInstance = new Singleton();
private Singletion(){}
public static Singleton getInstance(){
return uniqueInstance;
}
}
- 双检查加锁:如果十分关注性能,该方法能大大减少
getInstance
的耗时。
public class Singletion{
private volatile static Singletion uniqueInstance;
private Singleton(){
}
public static Singleton getInstance(){
if (uniqueInstace == null){
synchronized(Singletion.class){
if (uniqueInstance == null){
uniqueInstace = new Singleton();
}
}
}
return uniqueInstace;
}
}