Singleton Pattern :
限制類別只能產生一個實體並提供全域存取方法。
Singleton Pattern 大致上可以分為 2 種形式,飢餓型以及懶人型,而不論哪種形式都相當簡單。
飢餓型,在第3行宣告 sUniqueInstance 變數立即產生實體。
public class HungerSingleton
{
private static HungerSingleton sUniqueInstance = new HungerSingleton();
private HungerSingleton() {
}
public static HungerSingleton getInstance()
{
return sUniqueInstance;
}
}
懶人型,在第3行宣告 sUniqueInstance 變數並不馬上建立實體,而是等到需要實體才建立(第12行)。
public class LazySingleton
{
private static LazySingleton sUniqueInstance;
private LazySingleton(){
}
public static LazySingleton getInstance()
{
if (sUniqueInstance == null) {
sUniqueInstance = new LazySingleton();
}
return sUniqueInstance;
}
}
使用 Singleton Pattern 需要注意的是多執行緒的問題。
因為使用靜態初始化的關係,HungerSingleton 可以確保只產生一個 instance。
LazySingleton 由於使用判斷 null 值來產生實體,當 2 條執行緒同時執行到第11,12行,就有可能產生 2 個 Singleton ,就不是單例了。
最簡單的解決方式可以加上 synchronized 來限制同時只能有一條執行緒進入 getInstance 方法 如下
public static synchronized LazySingleton getInstance()
{
if (sUniqueInstance == null) {
sUniqueInstance = new LazySingleton();
}
return sUniqueInstance;
}
另一種比較麻煩的解決方法為DCL(double checked locking), 但這種解法有個限制就是只能支援 jvm 1.5之後的版本。
public class DCLSingleton {
private static volatile DCLSingleton sUniqueInstance;
private static Object mLock = new Object();
public static DCLSingleton getInstance() {
if (sUniqueInstance == null) {
synchronized (mLock) {
if (sUniqueInstance == null) {
sUniqueInstance = new DCLSingleton();
}
}
}
return sUniqueInstance;
}
}
如果情況予許還是選擇 HungerSingleton 來避免可能產生多個實體的情況,但 HungerSingleton 有另外一個缺點。
因為沒有實現 lazy load , 若其本身佔用大量記憶體,會在程序開始的時候造成浪費記憶體的情況。
可以使用靜態內部類別(static inner class)來實現 lazy load + HungerSingleton
public class InnerStaticHungerSingleton
{
private InnerStaticHungerSingleton()
{
//Prevents utility class being instantiated
}
private static class InnerInstance{
private static final InnerStaticHungerSingleton UNIQUE_INSTANCE = new InnerStaticHungerSingleton();
}
public static InnerStaticHungerSingleton getInstance()
{
return InnerInstance.UNIQUE_INSTANCE;
}
}
另外 LazySingleton 實現了 lazy load 作法。也就是為了節省記憶體使用空間,只有在變數需要實體才產生。
除了 lazy load 可以節省記憶體之外,還有另一個技術 cache 可以達到相同的效果。
simple cache in java
public class SimpleCache {
private Map<String, Object> mCache = new HashMap<String, Object>();
private static final String KEY_OF_DATA = "keyOfData";
public Object getCache() {
Object data = mCache.get(KEY_OF_DATA);
if (data == null) {
data = new Object();
mCache.put(KEY_OF_DATA, data);
}
return data;
}
}
簡單的 cache 實作,主要使用 Map 來達成,第一次呼叫 getCache 會進入第10行,並把 data 存入 map 中,隨後需要使用的時候再從 map 取出即可。
不需要重複產生實體,其實再複雜一點的變化型就是 Flyweight Pattern(享元模式),其作用也是解決需要大量細微性物件時,記憶體不夠的狀態。