標籤:
### 1. 概述
> 單例模式是確保某一個類中有且只有一個執行個體。
----------
### 2. 餓漢式單例
``` java
public class SingletonInstance {
private static SingletonInstance mInstance = new SingletonInstance();
// 預設私人構造方法
private SingletonInstance(){}
// 靜態Factory 方法
public static SingletonInstance getInstance(){
return mInstance ;
}
}
```
在餓漢式單例中,靜態變數會在私人構造方法中初始化,這時候唯一的執行個體就被建立出來了,餓漢式主要使用到的是**空間換時間**的思想,在類還在載入的時候,對象執行個體便已經建立好了。
----------
### 3. 懶漢式單例
```java
public class SingletonInstance {
private static SingletonInstance mInstance = null;
// 私人預設構造方法
private SingletonInstance(){}
// 靜態Factory 方法
public static synchronized SingletonInstance getInstance(){
if(mInstance == null){
mInstance = new SingletonInstance();
}
return mInstance;
}
}
```
對於懶漢式單例的處理,使用了``synchronized``參數修飾Factory 方法,用來在多線程環境中解決同步問題,懶漢式主要是使用到的是**時間換空間**的思想,在擷取執行個體的時候進行判斷,只有在需要的時候才去建立對象,節省記憶體空間,但是缺點就是實現的方式是安全執行緒的,這樣會降低訪問的速度。
----------
### 4. 雙重加鎖單例
```java
public class SingletonInstance {
private volatile static SingletonInstance mInstance = null;
private SingletonInstance(){}
public static SingletonInstance getInstance(){
//先檢查執行個體是否存在,如果不存在才進入下面的同步塊
if(mInstance == null){
//同步塊,安全執行緒的建立執行個體
synchronized (SingletonInstance .class) {
//再次檢查執行個體是否存在,如果不存在才真正的建立執行個體
if(mInstance == null){
mInstance = new SingletonInstance ();
}
}
}
return mInstance;
}
}
```
雙重加鎖機制指的是,在每次進入``getInstance``方法先不同步,而是進入方法後,先檢查執行個體是否存在,如果不存在才進行下面的同步塊,這屬於第一重檢查,進入同步塊過後再檢查執行個體是否存在,如果不存在,就在同步的情況下建立一個新的執行個體,這屬於第二重檢查。這樣便只需要同步一次,並減少了在多次同步情況下進行判斷浪費的時間。
這種實現方式會使用到**volatile**關鍵字,意思是被**volatile**修飾的變數的值,不會被本地線程緩衝,所有對該變數的讀寫都是直接操作共用記憶體,從而保證線程正確的處理該變數。
> **volatile**關鍵字在**JDK5**之前的版本中加鎖失敗,注意之。而且**volatile**關鍵字會屏蔽掉虛擬機器中的一些必要的代碼最佳化,因此雖然能實現雙重檢查加鎖機制的單例,但並不建議大量採用。
那有什麼方案可以既能達到消極式載入,又能實現安全執行緒的目的呢?
-----
### 5. Lazy Initialization Holder Class模式
```java
public class SingletonInstance {
private SingletonInstance(){}
/**
* 類級的內部類的執行個體與外部類的執行個體沒有綁定關係,而且只有被調用到時才會裝載
*/
private static class SingletonHolder{
/**
* 靜態初始化,由JVM來保證安全執行緒
*/
private static SingletonInstance mInstance = new SingletonInstance();
}
public static SingletonInstance getInstance(){
return SingletonHolder.mInstance;
}
}
```
如果只是想簡單的實現安全執行緒的單例,可以使用之前的**餓漢式**方式。但是缺點就是會在類裝載的時候初始化對象,造成空間的浪費。
那麼只要解決了類載入時自動初始化對象的問題,便可以解決問題。因此可以採用類級內部類的方式去實現,這樣的話,只有在需要的時候才會建立對象執行個體,也達到了消極式載入和安全執行緒的目的。
從實現過程來看,但調用`getInstance`方法時,它會讀取`SingletonHolder.mInstance`,從而初始化,在這個類被裝載的時候,也會初始化靜態成員,而由於靜態域的特性,只會初始化一次,並且由JVM來保證安全執行緒。
>- 什麼是類級內部類?
被`static`修飾的成員內部類才是類級內部類,如果沒有被`static`修飾則被稱為對象內部類,而且類級內部類與外部類對象不存在依賴關係,只有在第一次使用的時候才會被調用。
>- 多線程預設同步鎖知識?
在多線程開發中,我們主要使用`synchronized`來對互斥鎖進行同步控制,但是某些情況下JVM已經為我們進行了同步控制了,主要有:
1. 靜態初始化方法初始化資料時;
2. 訪問`final`欄位時;
3. 在建立線程之前建立對象時;
4. 線程可以看見要處理的對象時;
----------
### 6. 枚舉式單例
```java
public enum SingletonInstace{
// 定義一個枚舉元素,它代表了一個執行個體
mInstance;
// 單例的操作
public void singletonOperation(){
}
}
```
使用枚舉的方式既使得代碼簡潔,而且也由JVM來保證序列化機制,防止多次執行個體化,是最佳的實現單例的方式。
【Java設計模式】單例模式