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

煙花散盡13141發表於2022-06-06

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必須存在否則執行緒不安全。

相關文章