如果說道設計模式,我想大家想到最多的應該就是單例模式和工廠模式,上一篇文章中詳細介紹了工廠模式以及簡單工廠模式和抽象工廠模式。相對工廠模式,單例模式要簡單一點,但是單例模式也同樣有很多值得思考的問題,下面就來看一下這些問題以及解決的辦法。
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