使用單例模式,可以確保在整個系統中僅有該類的一個實例。此外,單例模式的類會提供一個訪問類實例的全局訪問點。相比使用關鍵字 new來直接實例化一個對象 ,使用單例模式,將對象的創建"委托"給了類的一個靜態方法。在該靜態方法中實現對象的實例化並將其返回,同時該方法的執行過程也提供了控制實例化對象的時機。 ...
單例模式
使用單例模式,可以確保在整個系統中僅有該類的一個實例。此外,單例模式的類會提供一個訪問類實例的全局訪問點。相比使用關鍵字 new來直接實例化一個對象 ,使用單例模式,將對象的創建"委托"給了類的一個靜態方法。在該靜態方法中實現對象的實例化並將其返回,同時該方法的執行過程也提供了控制實例化對象的時機。
1. 定義
該模式確保一個類只有一個實例,並提供一個全局訪問點。
2. 為什麼需要
對於一些類來說,只有一個實例是很重要的,且有些類只應該存在一個實例。比如,線程池、緩存、日誌對象等。
對於一些可以共用,並且非常耗費資源的對象,如果實例化多個對象,很顯然會加重系統的負擔。此外,它可能也不會帶來收益,更甚可能引發一些問題。而對於那些在業務領域本應該只存在一個實例的領域對象,使用單例模式就更是理所當然了。
就好比,國家規定了“一夫一妻”制度,如果你非得 new Wife(); 兩次,那必然你的系統就會出問題。如果,在一個“國家”的作用域範圍內,實例化了兩個"主席"對象,那也會導致系統出現問題。
3. 實現方案
雖然,單例模式實現的功效較為簡單,但是在不同的場景下也提供了不一樣的實現機制。
3.1 “延遲”式單例
它的實例化對象只有在真正需要的時候,才會被創建。代碼如下:
public class LazySingleton { private static LazySingleton singleton; //使用靜態對象來存儲唯一實例 /* 該構造器的訪問描述符為 private,所以使得其他的類不可以通過構造器來直接實例化該對象。 那麼怎麼來構造該類的對象呢? 通過下麵提供的靜態方法,在其中通過調用這個私有的構造器來實例化對象。與此同時,在該方法中控制使得該類只能有一個實例化對象。 當然,單例的構造器也是可以傳入參數的。只需要在下麵的實例化方法getInstance()中傳入構造對象需要的參數,即可。 */ private LazySingleton() { } /* 該方法實現了對象的構造,並控制使得只能構造一個實例化對象。 */ public static LazySingleton getInstance() { if (singleton == null) { singleton = new LazySingleton(); } return singleton; } }
想要獲得該單例對象,只需要使用靜態方法 getInstance() 即可。該方法在多線程場景下不能保證只創建一個實例對象。如果存在兩個線程A,B,且都需要獲取該單例對象,則存在如下可能:
- 系統初始 singleton = null; 線程A和B分別調用 LazySingleton.getInstance(); 方法。
- 線程A執行 if 判斷語句,條件為 true ,進入 if 代碼塊。且線程調度切換到線程B執行。
- 線程B執行 if 判斷語句,此時 singleton 依然為 null ,因此進入 if 語句塊,並實例化了一個單例對象,並返回。
- 切換到線程A,線程A直接調用 new 構造另一個單例對象,並返回。
- 此時,A、B線程返回不同的實例對象。
3.2 “饑餓”式單例
對於某些對象,如果在創建和運行時的負擔不太重,你想要急切的創建此單例,那麼可以使用該方式來實現單例。代碼如下:
public class HungrySingleton { private static HungrySingleton singleton = new HungrySingleton(); // 直接創建唯一實例 private HungrySingleton() { } /* 獲取對象的唯一訪問點 */ public static HungrySingleton getInstance() { return singleton; // 每次直接使用已創建的實例對象返回 } }
該方法在靜態初始化中創建單例對象,可以保證線程安全。
3.3 “雙重檢查加鎖”式單例
該方法可以避免在多線程場景下,在系統中出現單例類的多個實例的情況。代碼如下:
public class DoubleCheckSingleton { private volatile static DoubleCheckSingleton singleton; // volatile 修飾符保證變數在多線程之間的可見性 private DoubleCheckSingleton() {} public static DoubleCheckSingleton getInstance() { if (singleton == null) { // 檢查實例,如果不存在就進入同步區 synchronized (DoubleCheckSingleton.class) { if (singleton == null) { // 進入同步區後在檢查一次,如果還是為null,才創建對象 singleton = new DoubleCheckSingleton(); } } } return singleton; } }
這種方法,可以“最小化”同步代碼塊,可以減少時間的耗費,提高性能。
4. 總結
單例模式可能是所有設計模式中最簡單的一種了,但是它卻是實際應用中很有可能碰到的一種設計模式。它簡單,也簡約,但威力不小。它存在如下優點:
- 對唯一實例的受控訪問
- 縮小命名空間,該模式是對全局變數的一種改進,避免了使用那些存儲唯一實例的全局變數來污染命名空間。
- 允許對操作和表示的精化。
- 允許可變數目的實例。事實上,可以在 getInstance() 方法中控制實例化多個對象。對象數為1,可以視為一種特例。
孤獨是一個人的狂歡。在整個系統中,單例對象都只存在一個,相比存在很多同胞的 Person, Student 等類對象它是孤獨的,但這也突顯了它在系統的重要地位。它就好比“瀕危物種”。與此同時,所謂能力越大責任越大,因為沒有同伴來分擔壓力,所以所有與該類的"交流"都需要該單例對象來完成,這也證明瞭它的“能力強大”。
對於程式員而言,想要保持競爭力,事實上就是一個向著“單例”對象發展的過程。在團隊里,如果你在某方面是獨一無二的“對象”,那麼你自然就無可取代。
強行喂自己一口雞湯:只有先經歷一個人的孤獨磨煉,才能享受和別人一起狂歡。