背景:
- 最近在看java設計模式,看了幾篇關於“單例模式”的文章,決定自己實踐一遍整理一下記錄下來。
定義:
- 在它的核心結構中只包含一個被稱爲單例的特殊類。通過單例模式可以保證系統中一個類只有一個實例。
特點:
- 單例類只能有一個實例;
- 單例類必須自己創建自己的唯一實例;
- 單例類必須給其它對象提供這一實例。
要點:
- 私有構造方法;
- 指向自己實例的私有靜態引用;
- 以自己實例爲返回值的靜態的公有方法。
優點:
- 只存在一個實例,減少了內存的開支,特別是一個對象需要頻繁創建銷毀時,而且創建銷毀時性能又無法優化,單例模式的優勢就非常明顯;
- 由於單例模式只存在一個實例,所以減少了系統的性能開銷,當一個單例對象產生時如果需要比較多的資源時,則可以在應用啟動時 * 直接生產一個單例,然後永駐內存的方式來解決;
- 單例模式可以避免對資源的多重佔用,例如一個寫文件的操作,由於只存在一個實例在內存中,避免對同一個資源文件的同時寫操作;
- 單例模式可以在系統設置全局的訪問點,優化和共享資源訪問,例如,可以設計一個單列類,負責所有數據表的映射處理。
缺點:
- 單例模式一般沒有接口,擴展很困難,若要擴展,除了修改代碼基本上沒有第二種途徑可以實現;
- 隱式使用引起類結構不清晰;
- 單例對象如果持有Context,那麼很容易引起內存洩漏,此時需要傳給單例對象的Context最好是Application Context。
場景:
- 需要頻繁實例化然後銷燬的對象;
- 創建對象時耗時過多或者資源過多,但又經常用到的對象;
- 資源共享的情況下,避免由於資源操作時導致的性能損耗;
- 控制資源的情況下,方便資源之間的互相通信。
注意事項:
- 只能使用單例類提供的方法獲得單例對象,不能使用反射,否則會實例化出新對象;
- 不要做斷開單例類對象與類中靜態引用的危險操作;
- 多線程使用單例共享資源時,注意線程安全問題。
代碼示範(單例類SingletonDemo):
- 第1種:懶漢式(線程不安全)
- 代碼:
private static SingletonDemo instance; private SingletonDemo() { } public static SingletonDemo getInstance() { if (null == instance) { instance = new SingletonDemo(); } return instance; }
- 優缺點:
- 優點:在用戶第一次調用時纔去初始化,節約資源,起到了懶加載的作用。
- 缺點:第一次調用時稍慢,因爲要初始化,多線程不能正常工作。
- 代碼:
- 第2種:懶漢(線程安全)
- 代碼:
private static SingletonDemo instance; private SingletonDemo() { } public static synchronized SingletonDemo getInstance() { if (null == instance) { instance = new SingletonDemo(); } return instance; }
- 優缺點:
- 優點:在多線程中能很好的工作。
- 缺點:每次調用getInstance()時都需要進行同步,大部分時候都是用不到同步的,造成了資源的浪費,所以不建議使用。
- 代碼:
- 第3種:餓漢式(天生的線程安全)
- 代碼:
private static SingletonDemo instance = new SingletonDemo(); private SingletonDemo() { } public static SingletonDemo getInstance() { return instance; }
- 優缺點:
- 優點:餓漢式在創建類的同時就創建了靜態實例供對象系統調用,以後不再發生改變, 所以是線程安全的。
- 缺點:
- 類加載時就完成了初始化,沒有達到懶加載的效果;
- 在類加載時就初始化了實例,如果想在創建實例前調用某個方法給它傳遞一個參數就變不到了。
- 代碼:
- 第4種:DCL(雙重檢查模式)
- 代碼:
private volatile static SingletonDemo instance; private SingletonDemo() { } public static SingletonDemo getInstance() { if (null == instance) { synchronized (SingletonDemo.class) { } if (null == instance) { instance = new SingletonDemo(); } } return instance; }
- 實現原理:
- 代碼:
- 在getInstance()時對instance時進行了2次判空,第一次是爲了不必要的同步,第二次是爲了instance爲null * 才創建實例。
- 優缺點:
- 優點:DCL的優點是資源利用率高,第一次執行getInstance()時實例對象才被實例化,效率高。
- 缺點:第一次加載時反應稍慢一點,而且有失效的可能。
- 代碼:
private static class SingletonHolder { private static final SingletonDemo INSTANCE = new SingletonDemo(); } private SingletonDemo() { } public static final SingletonDemo getInstance() { return SingletonHolder.INSTANCE; }
- 實現原理:
- 第一次加載Singleton類時並不會初始化instance,只有第一次調用getInstance()方法時 * 虛擬機加載SingleHolder並初始化instance,這樣不僅能確保線程安全,也能保證實例的唯一性,很合理的單例模式。
- 代碼:
INSTANCE; public void whateverMethon() { }
- 優缺點:
- 優點:避免了線程安全,防止反序列化重新創建新的對象。
- 缺點:失去了類的一些特性,沒有延時加載。
- 這個沒看明白,不知道是我閱讀的資料有問題還是自己理解力差。希望看到的小夥伴能給我講一下,最好有可運行的代碼示例。感激涕零!!!
總結:
- 以上是閱讀了幾篇文章之後自己嘗試寫了幾個小例子,畢竟“紙上得來終覺淺,絕知此事要躬行”嘛。
參閱資料,非常感激:
- https://blog.csdn.net/itachi85/article/details/50510124
- https://blog.csdn.net/coder_pig/article/details/54411894
- https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247483786&idx=1&sn=072991244ec2f259c7049bc80317a728&scene=21#wechat_redirect