常見單例實現方式:
1.懶漢式
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
線程安全但每次調用都加鎖導致效率低
2.餓漢式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
使用靜態變量實現唯一性
但類裝載時就創建實例,雖然導致類裝載的原因有很多種,在單例模式中大多數都是調用 getInstance 方法
但是也不能確定有其他的方式(或者其他的靜態方法)導致類裝載
這時候初始化 instance 顯然沒有達到 lazy loading 的效果。
3.雙檢鎖/雙重校驗鎖(DCL,即 double-checked locking)
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
將同步鎖置於第一個判空之內,所以只有第一次實例化時對效率有影響
之所以需要第二個判空,是因爲進入第一個判空之後,同步鎖之前,有可能切換到另一個線程,這時候另一個線程的第一個判空依然是通過的,如果沒有第二個判空,便可能產生2個實例.
4.登記式/靜態內部類
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
這是對餓漢式的改進,使用靜態內部類來實現懶加載
5.枚舉
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
使用Singleton.INSTANCE調用實例
這種方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不僅能避免多線程同步問題,而且還自動支持序列化機制,防止反序列化重新創建新的對象,絕對防止多次實例化。
不過,由於 JDK1.5 之後才加入 enum 特性,用這種方式寫不免讓人感覺生疏,在實際工作中,也很少用。
總結
一般情況下,不建議使用懶漢式,建議使用餓漢式,畢竟裝載了單例類卻不調用單例方法的並不常見。
如果涉及到反序列化創建對象時,可以嘗試使用枚舉方式。
如果有其他特殊的需求,可以考慮使用雙檢鎖方式。