設計模式--單例模式 改進型懶漢式
這個解決方案的名字是Lazy initialization holder class。這個模式綜合運用了java的類級內部類和多執行緒預設同步鎖的知識。
先來補充一下基礎知識,以下內容來源自清華大學出版社的《研磨設計模式》。
先簡單看看類級內部類相關的知識
*什麼是類級內部類? 簡單點說,類級內部類指的是,有static修飾的成員式內部類。如果沒有static修飾的成員式內部類叫物件級內部類。 *類級內部類相當於其外部類的static成分,他的物件與外部類物件間不存在依賴關係,因此可直接建立,而物件級內部類的例項,是繫結在外部物件例項中的。 *類級內部類中,可以定義靜態的方法。在靜態的方法中只能夠引用外部類的中的靜態成員方法或者成員變數。 *類級內部類相當於其外部類的成員,只有在第一次被使用的時候才會被裝載。
再來看看多執行緒預設同步鎖的知識。 大家都知道,在多執行緒開發中,為了解決兵法問題,主要通過使用synchronized來加互斥鎖進行同步控制。但是在某些情況中,JVM已經隱含的執行了同步,這些情況下就不用自己再來進行同步控制了,這些情況包括: *由靜態初始化器(在靜態欄位上或static{}塊中的初始化器)初始化資料時 *訪問 final 欄位時 *再建立執行緒之前建立物件時 *執行緒可以看見他將要處理的物件時
由此想要很簡單的實現執行緒安全,可以採用靜態初始化器的方式,他可以由JVM來保證執行緒的安全性。比如第一節的餓漢式實現方式。但是這樣一來,會浪費一定的空間,因為這種實現方式,會在類裝載的時候就初始化物件,不管你需不需要。
如果現在有一種方法能夠讓類裝載的時候不會初始化物件,不就解決問題了?一種客卿的方式就是採用類級內部類,在這個累計內部類裡面去建立物件例項。這樣一來,只要不適用這個類級內部類,那就不會建立物件例項。從而同時實現延遲載入和執行緒安全。
來看一下程式碼:
/** * 懶漢式單例模式改進 * 實現延遲載入,快取 * Lazy initialization holder class * 這個模式綜合運用了java的類級內部類和多執行緒預設同步鎖的知識 * @author qian.xu * */ public class MySingleton2a { /** * 類級的內部類,也就是靜態的成員式內部類,該內部類的例項與外部類的例項 * 沒有繫結的關係,而且只有被呼叫到才會裝載,從而實現了延遲載入 * @author qian.xu * */ private static class Singleton{ /** * 靜態初始化器,用JVM來保證執行緒安全 */ private static MySingleton2a singleton = new MySingleton2a(); static { System.out.println("---->類級的內部類被載入"); } private Singleton(){ System.out.println("---->類級的內部類建構函式被呼叫"); } } //私有化建構函式 private MySingleton2a(){ System.out.println("-->開始呼叫建構函式"); } //開放一個公有方法,判斷是否已經存在例項,有返回,沒有新建一個在返回 public static MySingleton2a getInstance(){ System.out.println("-->開始呼叫公有方法返回例項"); MySingleton2a s1 = null; s1 = Singleton.singleton; System.out.println("-->返回單例"); return s1; } }
然後是客戶端程式碼:
/** * 懶漢式單例模式改進 * 實現了延遲載入 * MySingleton2 */ public static void myprint2a(){ System.out.println("---------------懶漢式單例模式改進--------------"); System.out.println("第一次取得例項(改進懶漢式)"); MySingleton2a s1 = MySingleton2a.getInstance(); System.out.println("第二次取得例項(改進懶漢式)"); MySingleton2a s2 = MySingleton2a.getInstance(); if(s1==s2){ System.out.println(">>>>>s1,s2為同一例項(改進懶漢式)<<<<<"); } System.out.println(); }
/** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //懶漢式 //myprint(); //餓漢式 //myprint2(); //懶漢式改進 myprint2a(); //登記式 //myprint3(); }
輸出結果為:
---------------懶漢式單例模式改進-------------- 第一次取得例項(改進懶漢式) -->開始呼叫公有方法返回例項 -->開始呼叫建構函式 ---->類級的內部類被載入 -->返回單例 第二次取得例項(改進懶漢式) -->開始呼叫公有方法返回例項 -->返回單例 >>>>>s1,s2為同一例項(改進懶漢式)<<<<<
根據列印結果我們看出,內部類被載入的時間在外部類呼叫建構函式後。也就是說,第一次裝載外部類的時候,內部類沒有被載入,一直到我們呼叫s1 = Singleton.singleton時,內部類才被載入(延遲載入),又因為他是靜態的域,因此只會在虛擬機器裝載類的時候初始化一次,並由虛擬機器來保證他的執行緒安全性。
這個解決方案的優勢在於:getInstance方法並沒有被同步,並且只是執行的一個域的訪問,因此延遲初始化並沒有增加任何訪問成本~ --------------------- 作者:藍蜘蛛 來源:CSDN 原文:https://blog.csdn.net/lanzhizhuxia/article/details/7924373 版權宣告:本文為博主原創文章,轉載請附上博文連結!