一:單列模式的定義
確保某個類只有一個實例。
二:線程安全
說起單列模式,不得不提線程安全,那么什么是線程安全?我們通過幾種方式來說說
1.多線程訪問時,采用了加鎖機制,當一個線程訪問該類的某個數據時,進行保護,其他線程不能進行訪問直到該線程讀取完,其他線程才可使用。不會出現數據不一致或者數據污染。就是線程安全的。
2.你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的。
3.一個類或者程序所提供的接口對于線程來說是原子操作,或者多個線程之間的切換不會導致該接口的執行結果存在二義性,也就是說我們不用考慮同步的問題,就是線程安全的。
三:單列模式的幾種寫法
1:惡漢單列
優點:寫法比較簡單,在類裝載的時候就完成實例化。避免了線程同步問題,天生線程安全。
缺點:類裝載的時候就完成實例化,一定程度上會造成資源的浪費。
public class Singleton {
private static final Singleton INSTANCE=new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return INSTANCE;
}
}
2:懶漢單列
優點:只有使用時,才被實例化。
缺點:效率低,第一次加載需要實例化,反應稍慢。每次調用getInstance方法都會進行同步,消耗不必要的資源。
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
3:雙重檢查單列
優點:資源利用率高,效率高。
缺點:第一次加載時反應稍慢。某些情況下失效。
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
4:靜態內部類單列(推薦使用)
優點:保證了線程安全,資源利用率高,效率高。
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
5:枚舉單列(推薦使用)
優點:寫法簡單,任何情況下都是一個單列。前面幾種方法會在反序列化的時候,重新創建對象。
缺點:實例化一個單例類的時候,必須要記住使用相應的獲取對象的方法。并且枚舉在SDK1.5中才添加。
public enum SingletonEnum {
INSTANCE;
public void doSomething() {
}
}
五:其他方式
除了以上方式,還可以使用容器形式實現單列
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<>();
private SingletonManager() {
}
public static void putObject(String key, String instance){
if(!objMap.containsKey(key)){
objMap.put(key, instance);
}
}
public static Object getObject(String key){
return objMap.get(key);
}
}
四:備注
不管哪種方式實現,核心原理都是講構造函數私有化,通過靜態方法獲取一個唯一的實例,且保證線程安全、防止反序列化導致重新生成實例對象等問題。
實現方式取決項目本身。
另外,附上防止反序列化時重新生成對象方法:
private Object readResolve() throws ObjectStreamException {
return instance;
}