数据结构与算法--实现Singleton模式
生活随笔
收集整理的這篇文章主要介紹了
数据结构与算法--实现Singleton模式
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目:設計一個類,我們只生成該類的一個實例。
- 只生成一個實例的類就是實現Singleton(單例)模式的類型。本題其實主要考察我們設計模式,因為面試的時候先來一個簡單的,并且喜歡面設計模式相關的題目,而且,在常用的設計模式中,Singleton是比較簡單的,而且可以通過簡潔的代碼來實現。所有Singleton是常見的面試題目。
以下解題思路
- 由于只生成一個實例,我們可以將構造方法設置成私有構造,使得其他方法無法通過實例創建。我們可以定義一個靜態實例,在需要的時候創建該實例,如下思路:
解法一:只適用單線程
//不好解法一 /*** @author liaojiamin* @Date:Created in 10:18 2020/10/27*/ public class Singleton {private Singleton(){}public static Singleton singleton = null;public static Singleton getInstance(){if(null == singleton){singleton = new Singleton();}return singleton;} }- 分析,以上代碼是在不考慮并發的情況下的簡單單例模式,從下面幾點來保證了得到的實例是唯一的:
- 靜態實例:帶有static關鍵字的熟悉在每個類中都是唯一的(在class文件被加載到jvm中的準備階段,方法區為這些類變量進行內存分配,并且進行初始化。比如被static修飾的字段。非static修飾屬性會在類實例化時候在對內存中分配存儲空間。因此類變了在class文件被加載的時候才有,并不受實例化的影響)
- 私有構造方法限制客戶通過實例創建
- 提供getInstance唯一入口
- 以上代碼存在并發問題,用如下方法進行檢測:
解法二:并發安全,但是效率低
/*** @author liaojiamin* @Date:Created in 14:33 2020/10/27*/ public class SingletonOne {private SingletonOne(){}public static SingletonOne singletonOne;public static SingletonOne getInstance(){synchronized (SingletonOne.class){if(null == singletonOne){singletonOne = new SingletonOne();}return singletonOne;}} }- 區別在于獲取對象時,用synchronized關鍵字進行加鎖,同一時刻只能有一個線程執行,等第一個線程創建完時間后。第一個線程釋放同步鎖,此時第二個線程可以加上同步鎖,并允許接下來的代碼。這個時候,實例已經被第一個線程創建,所以第二個線程不會再重復創建實例,保證得到的是同一個實例(synchronized加鎖是一個非常耗時的操作,應該盡量避免)
可行的解法:加同步鎖前后兩次判斷
- 我們可以優化以上方法,只在我們需要的時候鏡像同步鎖,如下。
- 以上SingletonTwo中只有instance為null的時候,才需要加鎖。當instance已經創建出來后,無須加鎖。因為只有第一次instance為null,所以執行的結果就是只有第一次的時候才會加鎖,其他時候都無需鎖,所有效率高得多。
- 兩次判斷的原因:
- 假設我們去掉同步塊中的是否為null的判斷,有這樣一種情況,假設A線程和B線程都在同步塊外面判斷了synchronizedSingleton為null,結果A線程首先獲得了線程鎖,進入了同步塊,然后A線程會創造一個實例,此時synchronizedSingleton已經被賦予了實例,A線程退出同步塊,直接返回了第一個創造的實例,此時B線程獲得線程鎖,也進入同步塊,此時A線程其實已經創造好了實例,B線程正常情況應該直接返回的,但是因為同步塊里沒有判斷是否為null,直接就是一條創建實例的語句,所以B線程也會創造一個實例返回,此時就造成創造了多個實例的情況。
推薦的解法:利用靜態屬性
/*** @author liaojiamin* @Date:Created in 14:40 2020/10/27*/ public class SingletonThree {private SingletonThree(){}private static SingletonThree singletonThree = new SingletonThree();public static SingletonThree getInstance(){return singletonThree;} }- SingletonThree實現的方式簡潔。我們初始化靜態變量singletonThree 時候創建一個實例。由于Java是在類加載的時候在方法區對靜態屬性分配內存,并且只初始化一次,這樣我們就能保證只初始化一次singletonThree 。
- 因為SingletonThree 中singletonThree 的初始化并不是調用getInstance的時候創建的,而且在類加載的時候就已經創建,我們使用的時候調用getInstance方法他其實是不會創建新的實例,所有他會提前創建好實例,不管你之后是否需要
最優的解法:實現按需創建實例
/*** @author liaojiamin* @Date:Created in 15:45 2020/10/27*/ public class SingletonFour {private SingletonFour(){}private static class SingletonInstance{static SingletonFour singletonFour = new SingletonFour();}public static SingletonFour getInstance(){return SingletonInstance.singletonFour;} }- SingletonFour 中我們定義了一個私有的內部類SingletonInstance我們利用:**內部靜態類不會自動初始化,只有調用靜態內部類的方法,靜態域,或者構造方法的時候才會加載靜態內部類。**的特點來實現的此處的單例
- 由于靜態內部類是私有的,只有我們在調用getInstance方法的時候被用到,因此當我們試圖通過屬性SingletonFour .getInstance得到SingletonFour 時候,會自動調用內部類SingletonInstance的靜態構造方法創建實例,并初始化內部類中的靜態變量 singletonFour
解法比較
- 以上五種實現方案中,第一張方法在多線程環境中不能正常工作,第二種線程安全的方法但是實際效率低下,都不是我們所期待的可運行的解法。第三種方法中,我們通過兩次判斷加一次鎖確保在多線程環境能高效運轉。第四種利用java靜態屬性的特性,確保值創建一個實例。第五種方法利用私有嵌套類型的特性,做到只在真正需要的時候才創建實例,提高空間使用率。第五種解法是最優解。
下一篇: 數據結構與算法–數組:二維數組中查找
總結
以上是生活随笔為你收集整理的数据结构与算法--实现Singleton模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 病毒性脑炎严重吗
- 下一篇: 数据结构与算法--数组:二维数组中查找