## **單例模式的定義與特點**
定義:指一個類只有一個實例,且該類能自行創建這個實例的一種模式
目的:為了節省內存資源、保證數據內容的一致性等
特點:
1. 單例類只有一個實例對象
2. 該單例對象必須由單例類自行創建
3. 單例類對外提供一個訪問該單例的全局訪問點
## **單例創建方式**
1. 餓漢式:類初始化時,會立即加載該對象,線程天生安全,調用效率高,項目中常用的模式。
```
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}
```
2. 懶漢式:類初始化時,不會初始化該對象,真正需要使用的時候才會創建該對象
```
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public synchronized static Singleton getInstance() {
if (null == singleton) {
singleton = new Singleton();
}
return singleton;
}
}
```
3. 靜態內部方式:結合了懶漢式和餓漢式各自的優點,真正需要對象的時候才會加載,加載類是線程安全的
```
public class Singleton {
private Singleton() {
}
// 靜態內部類
private static class SingletonInstance {
private static Singleton singleton = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.singleton;
}
}
```
4. 枚舉單例:使用枚舉實現單例模式(枚舉本身就是單例)
5. 雙重檢測鎖方式:懶漢模式的升級版
懶漢模式中加入synchronized鎖,每次獲取單例對象時都有讀取鎖的操作,性能必定大大降低,我們的目的只是想要在第一次創建實例的時候加鎖就可以了,所以改造代碼
~~~
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (null == singleton) {
synchronized (Singleton.class) {
if (null == singleton) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
~~~
初始化之后,后續再調用就不會有獲取鎖的操作,解決了性能的問題。看似很完美了,但是似乎哪里還有不多的地方,問題出在 singleton = new Singleton();這一過程可以分解成一下3步:
```
1.分配內存空間
2.初始化對象
3.將對象指向剛分配的內存空間
```
多線程的情況的處理器會進行重排序,有可能執行順序變成了1->3->2,那么多線程下的調用結果就變成了
| Thread A | Thread B |
| --- | --- |
| 檢查到`singleton`為空 | |
| 獲取鎖 | |
| 再次檢查到`singleton`為空 | |
| 為`singleton`分配內存空間 | |
| 將`singleton`指向內存空間 | |
| | 檢查到`singleton`不為空 |
| | 訪問`singleton`(此時對象還未完成初始化) |
| 初始化`singleton` | |
最終 Thread B訪問到了一個未初始化的對象,于是繼續改善代碼加入`volatile`關鍵字防止重排序
~~~
public class Singleton {
private volatile static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (null == singleton) {
synchronized (Singleton.class) {
if (null == singleton) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
~~~