簡單來說,單例模式指在應用的整個生命週期中,只存在一個實例對象。由此可知,單例模式可用於那些整個應用中只存在一個對象的場景,如配置文件、線程池、緩存、例子對象、工具類等。如果創造出多個對象,會出現很多問題,如佔用過多資源、不一致的結果等。
UML類圖
根據是否lazy初始化,可將單例模式分爲“餓漢”和“懶漢”式實現。
“餓漢”式實現
“餓漢”式實現 會在整個應用啓動初始化時,就會創建一個實例對象。“餓漢”式實現 是線程安全的,但是由於在應用啓動時創建,所以會使得應用啓動時間變長。
常規實現
public class Singleton {
// 靜態變量,聲明並初始化
private static Singleton instance = new Singleton();
// 私有化構造方法
private Singleton() {
}
// 對外提供示例獲取的靜態方法
public static Singleton getInstance() {
return instance;
}
}
實現簡單,線程安全。
枚舉實現
public enum Singleton {
INSTANCE;
public void method_name() {
}
...
}
這種方式是絕對線程安全的,並支持自動序列化機制。
“懶漢”式實現
“懶漢”式實現 在應用啓動時不會立即創建一個實例對象,是在首次被請求調用獲取實例對象的方法時才創建對應的實例對象,所以應用啓動過程相對“餓漢”式實現會快一點。
簡單實現
public class Singleton {
// 靜態變量,僅聲明
private static Singleton instance;
// 私有化構造方法
private Singleton() {
}
// 對外提供示例獲取的靜態方法
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
多線程環境下,非線程安全。
常規實現
public class Singleton {
// 靜態變量,僅聲明
private static Singleton instance;
// 私有化構造方法
private Singleton() {
}
// 對外提供示例獲取的靜態方法,使用Synchronized加鎖
public static Synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
多線程情況下,線程安全,但加鎖 synchronized影響效率。
雙重鎖實現
雙檢鎖/雙重校驗鎖(DCL,即 double-checked locking),實現示例如下:
public class Singleton {
// 靜態變量,僅聲明;volatile保證及時可見性
private volatile static Singleton instance;
// 私有化構造方法
private Singleton() {
}
// 對外提供示例獲取的靜態方法
public static Singleton getInstance() {
if(instance == null) {
// 檢測到未創建時,才加鎖保證創建過程是互斥和同步的
Synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
線程安全;雙重鎖校驗,可保證多線程下的高性能。
靜態內部類實現
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
// 私有化構造方法
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
線程安全;這種實現使用了classloader機制,達到的效果和雙重鎖實現差不多。
總結
一般情況下,不建議使用 簡單實現 和 常規實現 方式,建議使用“餓漢”式實現。只有在要明確實現 lazy loading 效果時,使用靜態內部類的方式更佳。如果涉及到反序列化創建對象時,可以嘗試使用枚舉實現方式。如果有其他特殊的需求,可以考慮使用雙重鎖校驗方式。