如果说道设计模式,我想大家想到最多的应该就是单例模式和工厂模式,上一篇文章中详细介绍了工厂模式以及简单工厂模式和抽象工厂模式。相对工厂模式,单例模式要简单一点,但是单例模式也同样有很多值得思考的问题,下面就来看一下这些问题以及解决的办法。
1、静态初始化实例对象(网上也有人叫饥饿实例化)
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {
}
public static EagerSingleton getInstance() {
return EagerSingleton.instance;
}
}
2、当第一次需要的时候创建实例化对象(懒汉实例化)
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() {
}
public synchronized static LazySingleton getInstance() {
if(instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
这里一定要注意加synchronized,否则就会引进多线程下的不安全的情况了。
但是,考虑到synchronized加在方法上,锁的粒度太多,会对性能产生影响。所以引入下面的dcl算法进行改进。
3、采用dcl算法改进2(关于dcl请看这个http://en.wikipedia.org/wiki/Double-checked_locking,后面找时间详细聊聊这个东西,貌似是jee中一个很常用的东西)
public class DclLazySingleton {
private static DclLazySingleton instance = null;
private DclLazySingleton() {
}
public static DclLazySingleton getInstance() {
if(instance == null) {
synchronized(DclLazySingleton.class) {
if(instance == null) {
instance = new DclLazySingleton();
}
}
}
return instance;
}
}
ps:按照wiki的说法,这种算法是有问题,这个后面在讨论,不过貌似异常出现的概率很小,而且很多成功的开源项目都用到了它,所以,不用担心!
4、看到这里,是不是大家觉得单例模式就该解决了呢?可是有没有发现一个问题呢?上面的单例类都不能做父类,因为构造方法是私有的,ps,子类的构造方法默认会去调用父类的构造方法,如果是私有的,这个果断没法继承啊。
所以就引进了下面的注册式的单例实现方式。看网上很多人都说spring中使用了这种方式,所以开始了解这种方式,其实经典的GoF的书中就有对这个的介绍。
public class RegisterSingleton {
private static Map<String, RegisterSingleton> instanceMap = new HashMap<String, RegisterSingleton>();
static {
RegisterSingleton instanceThis = new RegisterSingleton();
instanceMap.put(RegisterSingleton.class.getName(), instanceThis);
}
protected RegisterSingleton() {
}
public static RegisterSingleton getInstance(String name) {
if(name == null) {
name = RegisterSingleton.class.getName();
}
if(instanceMap.get(name) == null) {
try {
instanceMap.put(name, (RegisterSingleton)Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return instanceMap.get(name);
}
public static RegisterSingleton getInstance() {
return getInstance(RegisterSingleton.class.getName());
}
}
使用重载来便捷的调用getInstance()方法。
子类如果继承去调用getInstance方法呢?请看如下代码:
public class RegisterSingletonChild extends RegisterSingleton {
protected RegisterSingletonChild() {}
public static RegisterSingletonChild getInstance() {
return (RegisterSingletonChild)RegisterSingleton.getInstance(RegisterSingletonChild.class.getName());
}
}
当然,如果子类确定没有子类的话,则可以考虑用private去替代protected.
5、总结
以上4种方式基本包含了单例模式使用的四种方法,考虑了多线程下单例模式的线程安全性和有继承情况下的注册式单例实现。
老规矩上pdf,呵呵。/Files/zhenxuanpan/设计模式之单例模式的详解.pdf