日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构与算法--实现Singleton模式

發布時間:2023/12/4 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构与算法--实现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:44 2020/10/27*/ public class TestSingletonRunnableMain {private Boolean lock;public Boolean getLock() {return lock;}public void setLock(Boolean lock) {this.lock = lock;}public static void main(String[] args) throws InterruptedException {Long startTime = System.currentTimeMillis();int num = 100;final CyclicBarrier cyclicBarrier = new CyclicBarrier(num);final Set<String> set = Collections.synchronizedSet(new HashSet<String>());ThreadFactory nameThreadFactory = new ThreadFactoryBuilder().setNameFormat("nameThreadFactory-01").build();ExecutorService executorService = new ThreadPoolExecutor(100, 100, 1,TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), nameThreadFactory, new ThreadPoolExecutor.CallerRunsPolicy());for (int i = 0; i < num; i++) {executorService.execute(new Runnable() {@Overridepublic void run() {try {cyclicBarrier.await();Singleton singletonThree = Singleton.getInstance();set.add(singletonThree.toString());} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}});}Thread.sleep(2000);System.out.println("in more thread get singleton");for (String s : set) {System.out.println(s);}executorService.shutdown();} } //輸出: //in more thread get singleton //com.ljm.resource.math.Singleton@53fe75ec //com.ljm.resource.math.Singleton@4eba943c
解法二:并發安全,但是效率低
/*** @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加鎖是一個非常耗時的操作,應該盡量避免)
可行的解法:加同步鎖前后兩次判斷
  • 我們可以優化以上方法,只在我們需要的時候鏡像同步鎖,如下。
/*** @author liaojiamin* @Date:Created in 14:38 2020/10/27*/ public class SingletonTwo {private SingletonTwo(){}public static SingletonTwo singletonTwo;public static SingletonTwo getInstance(){if(null == singletonTwo){synchronized (SingletonOne.class){if(null == singletonTwo){singletonTwo = new SingletonTwo();}}}return singletonTwo;} }
  • 以上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模式的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。