從單例談double-check必要性,多種單例各取所需


theme: fancy

前言

  • 前面鋪掉了那麼多都是在講原則,講圖例。很多同學可能都覺得和設計模式不是很搭邊。雖說設計模式也是理論的東西,但是設計原則可能對我們理解而言更加的抽象。不過好在原則東西不是很多,後面我們就可以開始轉講設計模式了。
  • 我的思路是按照設計模式進行分類整理。期間穿插相關的知識進行擴展從而保證我們學習的更加的全面。在正式開始前我現在這裏立個Flag。爭取在20周內完成我們設計模式章節的內容。期間可能會有別的學習,20周爭取吧
  • 相信單例模式是大家第一個使用到的設計模式吧。不管你怎麼樣,我第一個使用的就是單例模式。其實單例模式也是分很多種的【餓漢式】、【懶漢式】。如果在細分還有線程安全和線程不安全版本的。

餓漢式

  • 顧名思義餓漢式就是對類需求很迫切。從Java角度看就是類隨着JVM啓動就開始創建,不管你是否使用到只要JVM啓動就會創建。
 public class SingleFactory
 {
     private static Person person = new Person();
 ​
     private SingleFactory()
     {
     }
 ​
     public static Person getInstance()
     {
         return person;
     }
 }
  • 上面這段代碼就是餓漢式單例模式。通過這單代碼我們也能夠總結出單例模式的幾個特點
  • 特點
    隱藏類的創建即外部無法進行創建
    內部初始化好一個完整的類
    提供一個可以訪問到內部實例的方法,這裏指的是getInstance

image-20220509183514066.png

  • 單例模式特點還是很容易區分的。餓漢式感覺挺好的,那爲什麼後面還會出現懶漢式及其相關的變形呢?下面我們就來看看餓漢式有啥缺點吧。
  • 首先上面我們提到餓漢式的標誌性特點就是隨着JVM 的啓動開始生成實例對象。這是優點同時也是缺點。大家應該都用過Mybatis等框架,這些框架爲了加快我們程序的啓動速度紛紛推出各種懶加載機制。
  • 何爲懶加載呢?就是用到的時候再去初始化相關業務,將和啓動不相關的部分抽離出去,這樣啓動速度自然就快了起來了。在回到餓漢式,你不管三七二十一就把我給創建了這無疑影響了我的程序啓動速度。如果這個單例模式你使用了倒還好,假如啓動之後壓根就沒用到這個單例模式的類,那我豈不是喫力不討好。不僅浪費了時間還浪費了我的空間。
  • 所以說,處於對性能的考慮呢?還是建議大家不要使用餓漢式單例。但是,存在即是合理的,我們不能一棒子打死一堆人。具體場景具體對待吧XDM。

🐶變形1

 public class SingleFactory
 {
     private static Person person ;
 ​
     static {
         person = new Person();
     }
 ​
     private SingleFactory()
     {
     }
 ​
     public static Person getInstance()
     {
         return person;
     }
 }
  • 咋一看好像和上面的沒啥區別哦。仔細對比你就會發現我們這裏並沒有立刻創建Person這個類,而是放在靜態代碼塊中初始化實例了。
  • 放在靜態代碼塊和直接創建其實是一樣的。都是通過類加載的方式來進行實例化的。基本同根同源沒啥可說的 。
  • 關於Static關鍵字我們之前也有說過,他涉及到的是類加載的順序。我們在類加載的最後階段就是執行我們的靜態代碼塊

懶漢式

 public class SingleFactory
 {
     private static Person person = null;
 ​
     private SingleFactory()
     {
     }
 ​
     public static Person getInstance()
     {
         try
         {
             Thread.sleep(30);
         }
         catch (InterruptedException e)
         {
            e.printStackTrace();
         }
         if(person==null){
             person=new Person();
         }
         return person;
     }
 }
  • 懶漢式就是將我們的對象創建放在最後一刻進行創建。並不是跟隨類加載的時候生成對象的,這樣會造成一定程度的內存浪費。懶漢式更加的提高了內存的有效利用。在getInstance方法中我們在獲取對象前判斷是否已經生成過對象。如果沒有在生成對象。這種行爲俗稱懶,所以叫做懶漢式單例模式

🐱變形1

  • 上面懶漢式單例中我加入了睡眠操作。這是因爲我想模擬出他的缺點。上面這種方式在高併發的場景下並不能保證系統中僅有一個實例對象。
 public class SingleFactory
 {
     private static Person person = null;
 ​
     private SingleFactory()
     {
     }
 ​
     public static Person getIstance()
     {
         try
         {
             Thread.sleep(30);
         }
         catch (InterruptedException e)
         {
             e.printStackTrace();
         }
         synchronized (SingleFactory.class)
         {
             if (person == null)
             {
                 person = new Person();
             }
         }
         return person;
     }
 }
  • 只需要加一把鎖,就能保證線性操作了。但是仔細想想難道這樣就真的安全了嗎。

double-check

  • 在多線程下安全的單例模式應該非double-check莫屬了吧。
 public class OnFactory {
     private static volatile OnFactory onFactory;
 ​
     public static OnFactory getInstance() {
         if (null == onFactory) {
             synchronized (OnFactory.class) {
                 if (null == onFactory) {
                     onFactory = new OnFactory();
                 }
             }
         }
         return onFactory;
     }
 }
  • 這段代碼是之前咱們學習double-check和volatile的時候寫過的一段代碼。在這裏我們不僅在鎖前後都判斷了而且還加上了volatile進行內存刷新。關於volatile需要的在主頁中搜索關鍵詞即可找到。這裏僅需要知道一點volatile必須存在否則線程不安全。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章