『單例模式』是一種建立型的設計模式,保證一個類只有一個實例,並提供一個訪問它的全局訪問點。java
在一個系統中,一個類常常會被使用在不一樣的地方,經過單例模式,咱們能夠避免屢次建立多個實例,從而節約系統資源。git
單例模式每每有三個特徵,一個類只能有一個實例,它必須自行提供實例的建立,它必須提供方法暴露此實例。github
餓漢老是一次吃個飽,因此這種方式老是在系統初始化的時候建立全部的對象,無論會不會什麼時候被使用。面試
public class SingleTon {
//自行構造實例
private static final SingleTon instance = new SingleTon();
//置空構造器,不容許外部構造
public SingleTon(){}
//對外暴露內部實例
public SingleTon getInstance(){
return this.instance;
}
}
複製代碼
餓漢方式實現的單例模式是極其簡單的,但缺點也很明顯,即使這個類一時半會不會被使用到,但也必須在編譯的時候初始化分配堆內存,建立這個內部實例。設計模式
懶漢很懶,只有在系統用到某個類的實例的時候,纔會實例化出一個惟一實例。安全
public class SingleTon {
private static SingleTon instance= null;
private SingleTon(){}
public static SingleTon getInstance(){
if(null == instance){
instance = new SingleTon();
}
return instance;
}
}
複製代碼
instance 在類編譯的時候沒有初始化,而只有在調用 getInstance 方法的時候,纔會去實例化 instance。微信
looks pretty!markdown
多線程環境下,線程 A 和線程 B 同時判斷 instance==null,都去實例化 instance,致使 instance 被實例化兩次,堆中產生一個無引用對象,併發量大的狀況下,會有更多的無用對象被建立,甚至可能提早觸發 GC。多線程
線程不安全,相信你第一時間也會想到加鎖控制,那你是否是也這麼加的呢?併發
public class SingleTonLock {
private static SingleTonLock instance= null;
public SingleTonLock(){}
public synchronized SingleTonLock getInstance(){
if (instance == null){
instance = new SingleTonLock();
}
return instance;
}
}
複製代碼
這種方式直接給 getInstance 方法加鎖了,很明顯,會形成大量無效的鎖等待,繼續優化。
public class SingleTonLock {
private static volatile SingleTonLock instance= null;
public SingleTonLock(){}
public SingleTonLock getInstance(){
if (instance == null){
synchronized(this){
//再次判斷是爲了防止有的線程醒來之後再次實例化
//有可能其餘線程已經實例化完成了
if (instance == null){
instance = new SingleTonLock();
}
}
}
return instance;
}
}
複製代碼
給 instance 加 volatile 修飾是爲了防止 jvm 指令重排序,經過再次判斷能夠保證此實例的惟一實例化。
這的確是一種不錯的懶漢實例,推薦你們使用,但我更推薦下一種。
我的認爲使用枚舉類實現懶漢單例模式是最佳實踐,枚舉類本質上是用靜態字段來實現的,例如:
public enum Color {
RED(), GREEN(), BLUE(), YELLOW();
}
複製代碼
javap 反編譯這個枚舉類獲得:
public final class com.example.test.lazy.Color extends java.lang.Enum<com.example.test.lazy.Color> {
public static final com.example.test.lazy.Color RED;
public static final com.example.test.lazy.Color GREEN;
public static final com.example.test.lazy.Color BLUE;
public static final com.example.test.lazy.Color YELLOW;
public static com.example.test.lazy.Color[] values();
public static com.example.test.lazy.Color valueOf(java.lang.String);
static {};
}
複製代碼
那麼,枚舉如何實現單例模式,上代碼:
public class SingleTonE {
public static SingleTonE getInstance(){
return SingleTonEnum.SINGLETON.getInstance();
}
private enum SingleTonEnum{
SINGLETON;
private SingleTonE instance;
SingleTonEnum(){
instance = new SingleTonE();
}
public SingleTonE getInstance(){
return this.instance;
}
}
}
複製代碼
只有當調用 getInstance 方法獲取實例的時候,纔會觸發枚舉類的加載,而後按照上面說的,生成一個靜態字段並初始化其內部的單例 instance,由於 jvm 保證只能一個線程進行類加載,因此整個過程看起來很是的簡單。
我的認爲,枚舉類實現單例模式是一種最佳實踐,推薦你應用到本身的項目。
近期會整理一個設計模式系列,分別講講 23 種設計模式,感興趣的能夠關注下哦~
關注公衆不迷路:有詩有酒有代碼。
公衆號回覆「1024」加做者微信一塊兒探討學習!
公衆號回覆「面試題」送你一份面試題以及做者的做答答案
每篇文章用到的全部案例代碼素材都會上傳我我的 github
github.com/SingleYam/o… 歡迎來踩!