一文帶你瞭解單例設計模式
阿新 • • 發佈:2020-06-24
1.基本介紹
單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種型別的設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。
這種模式涉及到一個單一的類,該類負責建立自己的物件,同時確保只有單個物件被建立。這個類提供了一種訪問其唯一的物件的方式,可以直接訪問,不需要例項化該類的物件。
2.實現方式
通常單例模式在Java語言中,有兩種構建方式:
- 懶漢式—執行緒不安全:最基礎的實現方式,執行緒上下文單例,不需要共享給所有執行緒,也不需要加synchronize之類的鎖,以提高效能。
- 懶漢式—執行緒安全:加上synchronize之類保證執行緒安全的基礎上的懶漢模式,相對效能很低,大部分時間並不需要同步
- 餓漢方式。指全域性的單例例項在類裝載時構建。 [2]
- 雙檢鎖式。在懶漢式基礎上利用synchronize關鍵字和volatile關鍵字確保第一次建立時沒有執行緒間競爭而產生多個例項,僅第一次建立時同步,效能相對較高
- 登記式。作為建立類的全域性屬性存在,建立類被裝載時建立
- 列舉。java中列舉類本身也是一種單例模式
三大要素:
- 私有的構造方法;
- 指向自己例項的私有靜態引用;
- 以自己例項為返回值的靜態的公有方法。
3.相關例項
①餓漢式單例(執行緒安全)
闡述:見名知義,“餓漢”說明它很“飢餓”,迫切需要找到吃的,這裡也就是我們所說的例項。為什麼說它是執行緒安全的呢?因為我們一開始就建立一個這樣的例項,其他執行緒在訪問前這個例項就已經建立完成,在類的生命週期中只建立一次,所以餓漢單例天生就是執行緒安全的。
// 餓漢式單例示例
class Singletone {
private static Singletone singleton = new Singletone();
// 懶漢式
private Singletone() {
}
// 保證返回一個例項
public static Singletone getSingleton() {
return singleton;
}
}
public class EHanShi {
public static void main(String[] args) {
Singletone singleton1 = Singletone.getSingleton();
Singletone singleton2 = Singletone.getSingleton();
System.out.println(singleton1==singleton2);
}
}
複製程式碼
輸出結果為: true
②懶漢單例(需要加雙重檢查鎖來保證執行緒安全)
闡述: 所謂懶漢模式,就是像一個“懶漢”一樣,需要用到建立例項了程式再去建立例項,不需要建立例項程式就“懶得”去建立例項。為什麼說它不是執行緒安全的呢?因為在高併發的情況下懶漢單例就可能建立多個,這樣不僅違背了單例的原則,也會在造成使用不同例項造成的執行緒安全問題,所以懶漢單例需要加鎖。
// 懶漢單例 執行緒不安全 需要雙重檢查鎖保證在JVM中只能有一個例項
class Singleton {
// 使用volatile禁止指令重排序
private static volatile Singleton singleton;
// 懶漢式
private Singleton() {
}
// 保證返回一個例項
public static Singleton getSingleton() {
if (singleton == null) { // 第一層鎖
synchronized (Singleton.class) {
if (singleton == null) { //第二層鎖
singleton = new Singleton();
}
}
}
return singleton;
}
}
public class LanHanShi {
public static void main(String[] args) {
Singleton s1 = Singleton.getSingleton();
Singleton s2 = Singleton.getSingleton();
System.out.println(s1 == s2);
}
}
複製程式碼
輸出結果為: true
4.總結
懶漢式和餓漢式區別點:
-
餓漢模式是典型的空間換取時間,當類裝載的時候就會建立類的例項,不管你用不用,先創建出來,然後每次呼叫的時候就不需要再判斷了,節省了執行時間。但如果一直沒有人呼叫,這種浪費的空間就不值得,特別是在空間不足的情況下。
-
懶漢模式是這是一種時間換空間的做法,要想執行緒安全,大家第一想到的便是下面這種方式,就是在getSingleton方法加上synchronized關鍵字,但是這種方式也有致命的缺點,那就是併發率太低。