Android學(xué)習(xí)之設(shè)計(jì)模式---單例模式

做好計(jì)劃,定期復(fù)盤(pán),感知責(zé)任,提高執(zhí)行力

1 單例模式初識(shí):

1.1 單例模式:

單例模式是指確保某一個(gè)類(lèi)只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例;那為什么要有單例模式呢?比如我們?cè)诖a中有一個(gè)網(wǎng)絡(luò)請(qǐng)求的實(shí)現(xiàn)類(lèi),那在業(yè)務(wù)需求的場(chǎng)景中,會(huì)有頻繁的網(wǎng)絡(luò)請(qǐng)求,那此時(shí)如果有一個(gè)網(wǎng)絡(luò)請(qǐng)求,就去new一個(gè)網(wǎng)絡(luò)請(qǐng)求的實(shí)現(xiàn)類(lèi),這樣可能會(huì)造成資源的浪費(fèi),所以引入了單例模式;

1.2 單例模式的特征或者說(shuō)創(chuàng)建一個(gè)單例模式類(lèi)的要求:

  • 構(gòu)造方法不對(duì)外開(kāi)放的(private修飾);
  • 建立一個(gè)類(lèi)靜態(tài)變量,持有一個(gè)自己的實(shí)例;
  • 通過(guò)一個(gè)靜態(tài)方法或者枚舉返回單列類(lèi)的對(duì)象;
  • 注意多線(xiàn)程的場(chǎng)景也要保證單例;
  • 如果單例可以序列化,那要注意單例對(duì)象在反序列化時(shí)不會(huì)重新創(chuàng)建對(duì)象;

1.3 單例模式的兩種類(lèi)型:

1.3.1 餓漢式單例模式

public class singletonMode {
      private singletonMode (){} //構(gòu)造方法private修飾,不對(duì)外開(kāi)放
      private static mSingleton = new singletonMode();//靜態(tài)變量實(shí)例化
      public static singletonMode getInstance(){//通過(guò)一個(gè)靜態(tài)方法返回單列類(lèi)的對(duì)象
          retrun mSingleton;
      
}

上面為餓漢式單例模式的實(shí)現(xiàn)方式,從代碼中可以看出,在類(lèi)加載的時(shí)候mSingleton就已經(jīng)被實(shí)例化了,無(wú)論用不用都會(huì)被加載,所以可能會(huì)造成資源浪費(fèi)或者加載緩慢(比如說(shuō)構(gòu)造方法中做的操作比較多);但是它是線(xiàn)程安全的;
1.3.2 懶漢式單例模式
針對(duì)餓漢式單例模式的缺點(diǎn),則引出了如下的懶漢式單例模式:

public class singletonMode2 {
      private singletonMode2 (){} //構(gòu)造方法private修飾,不對(duì)外開(kāi)放
      private static mSingleton2 = null();//靜態(tài)變量
      public static singletonMode2 getInstance(){//通過(guò)一個(gè)靜態(tài)方法返回單列類(lèi)的對(duì)象
          if(mSingleton2  == null)//當(dāng)實(shí)例對(duì)象為null的時(shí)候才去new
              mSingleton2 = new singletonMode2();
          retrun mSingleton2;      
}

以上可以解決餓漢式單例模式在類(lèi)加載的時(shí)候就實(shí)例化靜態(tài)變量,引起資源浪費(fèi)的問(wèn)題,此處是在用的時(shí)候通過(guò)getInstance去拿時(shí),才去new;但是通過(guò)仔細(xì)觀(guān)察,懶漢式單例模式存在線(xiàn)程不安全問(wèn)題,所以需要進(jìn)一步優(yōu)化;
優(yōu)化方式一:對(duì)getInstance靜態(tài)方法添加synchronized關(guān)鍵字

public class singletonMode3 {
      private singletonMode3 (){} //構(gòu)造方法private修飾,不對(duì)外開(kāi)放
      private static mSingleton3 = null();//靜態(tài)變量
      public static synchronized  singletonMode3 getInstance(){//通過(guò)一個(gè)靜態(tài)方法返回單列類(lèi)的對(duì)象
          if(mSingleton3  == null)//當(dāng)實(shí)例對(duì)象為null的時(shí)候才去new
              mSingleton3 = new singletonMode3();
          retrun mSingleton3;      
}

相比之下,線(xiàn)程安全提高,但是synchronized是加在靜態(tài)方法上的,同步鎖的顆粒度有點(diǎn)大,所以進(jìn)一步優(yōu)化;
優(yōu)化方式二:這就引出了 雙重校驗(yàn)DCL

public class singletonMode4 {
      private singletonMode4 (){} //構(gòu)造方法private修飾,不對(duì)外開(kāi)放
      private static mSingleton4 = null();//靜態(tài)變量
      public static singletonMode4 getInstance(){//通過(guò)一個(gè)靜態(tài)方法返回單列類(lèi)的對(duì)象
          if(mSingleton4  == null){//第一層校驗(yàn)
              synchronized(singletonMode4 .class){
                  if(mSingleton4 == null){//第二層校驗(yàn)
                      mSingleton4 = new singletonMode4();
                  }
              }
          }
          retrun mSingleton4;      
}

采用方式二則降低了同步鎖的顆粒度,同時(shí)也考慮了線(xiàn)程安全問(wèn)題;
注意:但是在JAVA虛擬機(jī)中,大家都知道m(xù)Singleton4 = new singletonMode4()一句代碼它主要包括三個(gè)內(nèi)容:

  • mSingleton4 實(shí)例分配對(duì)象
  • 調(diào)用singletonMode4的構(gòu)造方法,初始化成員字段
  • 將singletonMode4對(duì)象賦值給mSingleton4
    由于在JDK中會(huì)進(jìn)行指令重排,所以有可能會(huì)導(dǎo)致DCL失效問(wèn)題,所以在JDK1.5后引入了volatile禁止指令重排,進(jìn)而保證了DCL雙重檢測(cè)的有效性;

2 餓漢式與懶漢式單例模式的應(yīng)用:

餓漢式
在類(lèi)加載的時(shí)候就會(huì)實(shí)例化對(duì)象,無(wú)論是否會(huì)用到這個(gè)對(duì)象,都會(huì)加載。如果在構(gòu)造方法里寫(xiě)了性能消耗較大,占時(shí)較久的代碼,那么就會(huì)在啟動(dòng)的時(shí)候感覺(jué)稍微有些卡頓。
懶漢式
是延遲加載的方式,只有使用的時(shí)候才會(huì)加載。 并且有線(xiàn)程安全的考量。使用懶漢式,在啟動(dòng)的時(shí)候,會(huì)感覺(jué)到比餓漢式略快,因?yàn)椴](méi)有做對(duì)象的實(shí)例化。 但是在第一次調(diào)用的時(shí)候,會(huì)進(jìn)行實(shí)例化操作,感覺(jué)上就略慢。

在實(shí)際應(yīng)用中看業(yè)務(wù)需求,如果業(yè)務(wù)上允許有比較充分的啟動(dòng)和初始化時(shí)間,就使用餓漢式,否則就使用懶漢式

3 單例模式擴(kuò)展

3.1 單例模式的其他實(shí)現(xiàn)方式:

方式一:靜態(tài)內(nèi)部類(lèi)單例模式:

public class singletonMode5 {
      private singletonMode5 (){} //構(gòu)造方法private修飾,不對(duì)外開(kāi)放
      private static class singletonModeInner {
          private static mSingleton5 = new singletonMode5();
      }
      public static singletonMode5 getInstance(){//通過(guò)一個(gè)靜態(tài)方法返回單列類(lèi)的對(duì)象
          retrun singletonMode5.singletonModeInner.mSingleton5 ;   
      }   
}

方式二:使用枚舉:

public class Single {
    private Single(){} 
    public enum SingleEnum { 
          singleHandler; 
          private Single single; 
          private SingleEnum () { 
              single = new Single(); 
          }
          public Single getSingle() {
               return single;
           }
     }
    public static Single getInstacne() { 
          return SingleEnum.singleHandler.getSingle(); 
    } 
}

對(duì)于以上兩種實(shí)現(xiàn)方式,枚舉是線(xiàn)程安全的。另外還有一些比如說(shuō)通過(guò)容器的方式來(lái)實(shí)現(xiàn)單例,有興趣的可以進(jìn)一步了解一下;

3.2 可序列化的單例類(lèi)

如果我們的類(lèi)是可序列化的,那么在反序列化時(shí)會(huì)破壞單例,生成新的單列類(lèi);

private Object readResolve(){
        System.out.println("read resolve");
        return instance;//返回之前定義的單例類(lèi)對(duì)象
    }

這中情況,可以通過(guò)重寫(xiě)readResolve()方法,此方法中返回了單例類(lèi)的對(duì)象。具體readResolve解析可參考這篇文章:https://blog.csdn.net/weixin_45433031/article/details/115364766?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-1&spm=1001.2101.3001.4242
小結(jié):本章節(jié)了解了設(shè)計(jì)模式中的單例模式,單例模式在我們的實(shí)際開(kāi)發(fā)中是很常見(jiàn)的,大家可以根據(jù)自己業(yè)務(wù)需求,選擇不同的實(shí)現(xiàn)方式,那重點(diǎn)關(guān)注一下DCL雙重檢測(cè);

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容