【Java設計模式】單例模式


### 1. 概述
> 單例模式是確保某一個類中有且只有一個實例。java

----------
### 2. 餓漢式單例
``` java
public class SingletonInstance {
private static SingletonInstance mInstance = new SingletonInstance();
// 默認私有構造方法
private SingletonInstance(){}
// 靜態工廠方法
public static SingletonInstance getInstance(){
return mInstance ;
}
}
```
在餓漢式單例中,靜態變量會在私有構造方法中初始化,這時候惟一的實例就被建立出來了,餓漢式主要使用到的是**空間換時間**的思想,在類還在加載的時候,對象實例便已經建立好了。緩存

----------安全

### 3. 懶漢式單例
```java
public class SingletonInstance {
private static SingletonInstance mInstance = null;
// 私有默認構造方法
private SingletonInstance(){}
// 靜態工廠方法
public static synchronized SingletonInstance getInstance(){
if(mInstance == null){
mInstance = new SingletonInstance();
}
return mInstance;
}
}
```
對於懶漢式單例的處理,使用了``synchronized``參數修飾工廠方法,用來在多線程環境中解決同步問題,懶漢式主要是使用到的是**時間換空間**的思想,在獲取實例的時候進行判斷,只有在須要的時候纔去建立對象,節省內存空間,可是缺點就是實現的方式是線程安全的,這樣會下降訪問的速度。多線程

----------
### 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來保證序列化機制,防止屢次實例化,是最佳的實現單例的方式。內存

相關文章
相關標籤/搜索