《设计模式》中对Singleton模式的定义为:保证一个类仅有一个实例,并提供一个可以访问它的全局访问点。
Singleton模式的工作原理如下:
- 用一个方法来实例化所需对象。为了保证该方法是唯一能实例化此类型对象的方法,我们将这个类的构造函数定义为保护的或者私有的。
- 调用这个方法时,先要检查对象是否已经实例化了。如果已被实例化,那么该方法仅返回对该对象的引用;如果尚未实例化,该方法将实例化对象并返回对此新实例的一个引用。
根据上述原理,我们可以得到Singleton模式的本质在于所有使用所需对象的其他对象都使用同一个实例。Singleton模式的意图使我们只对所需对象实例化一次,无需用户再关心所需对象是否存在,即让所需对象自己负责只实例化一次。
单例模式一般代码如下:
1 public class Singleton {
2 static private Singleton instance = new Singleton();
3
4 private Singleton(){}
5
6 public static Singleton getInstance(){
7 return instance;
8 }
9 }
当ClassLoader加载类后Singleton的实例会在第一时间内创建,因此对于开销比较大的单体更通常的做法是将实例化推迟到使用它的时候,即惰性加载,代码如下:
1 public class Singleton {
2 static private Singleton instance = null;
3
4 private Singleton(){}
5
6 public static Singleton getInstance(){
7 if(instance == null) instance =new Singleton();
8 return instance;
9 }
10 }
这是在单线程中的情况,如果我们的代码要在多线程中运行呢?
咋看起来,只需同步化对Singleton对象是否已创建进行检查操作即可。但性能上来说不不划算,因为所有线程都必须等待关于对象是否存在的检查。因此可能想到的是如下解决方法:
1 if(instance == null) {
2 synchronized(Singleton.class){
3 instance = new Singleton();
4 }
5 }
但仍有可能两个调用都满足null的检查条件,然后再尝试同步化,最后还是可能会创建两个Singleton对象。解决办法是在同步检查后,再次检查实例是否已创建。
1 if(instance == null) {
2 synchronized(Singleton.class){
3 if(instance == null) instance = new Singleton();
4 }
5 }
这种方法被称为DCL(Double-Checked Locking模式)。但是我们很快发现,这种方法在C++中适用,但对Java不适用,原因在于Java编译器本身的优化工作会在构造方法实例化对象之前从构造方法返回指向该对象的引用。印在在Singleton对象真正完全构造之前,dosyn就可能完成了。那怎么办呢?
我们可以利用类装载程序来解决这个问题。
1 public class Singleton {
2 private static class Instance{
3 static final Singleton instance = new Singleton();
4 public static Singleton getInstance(){
5 return Instance.instance;
6 }
7 }
8 }
这个方案之所以奏效,是因为内部类(Instance)只被装载一次,所以只会创建一个Singleton对象。
参考:
1.《设计模式解析》Singleton模式和DCL模式
2.探索设计模式之六——单例模式
posted on 2012-07-27 01:04
hqjma 阅读(112)
评论(0) 编辑 收藏 所属分类:
Design Pattern