老王講設計模式(三)——單例模式

單例模式是一種經常使用的軟件設計模式。在它的核心結構中只包含一個被稱爲單例的特殊類。經過單例模式能夠保證系統中一個類只有一個實例。設計模式

早晨,老王來到公司,發現小蔡正對着電腦屏幕發呆,因而走到小蔡身後,發現小蔡正對着一個寶寶照發呆。安全

小蔡的屏幕

老王拍了一下小蔡肩膀,問:「這個是誰啊?沒據說你有男友啊。難道是私生子?」多線程

小蔡回頭,呸了老王一下,說:「這是我二表姑的侄子的姐夫的姑姑的妹妹的舅舅的兒媳婦的堂哥的娃娃。」併發

二表姑的侄子的姐夫的……

老王還沒回過神來,小蔡又說:「寶寶真可愛,要是我之後要寶寶了,我必定只要一個,我要帶她處處去耍,給他買各類漂亮的衣服,各類好玩的玩具,各類吧嗒吧嗒吧嗒……(此處省略數千字)」函數

老王心想:「都說一個女人等於500只鴨子,這個小蔡何止500只鴨子,簡直就是5000只鴨子,並且全是話癆型鴨子。」spa

老王的表情

老王趁小蔡兩句話之間喘氣的時機,趕忙打斷了小蔡:「你說到只要一個寶寶,我給你講講程序代碼只要一個寶寶的方法,好不?」線程

小蔡一聽,很好奇:「程序代碼,也有一個寶寶的說法?」設計

老王說眼看將話題轉移了,趕忙接着說:「是啊,那就是單例模式,它可讓整個程序系統裏,只存在惟一的實例對象。從而大幅節省內存資源。」代理

老王不給小蔡搭話的機會,緊接着演示起了代碼:「單例模式,分爲懶漢式和餓漢式,咱們先來看看懶漢式。」code

//懶漢式
public class Lazybones {

  //實例對象
  private static Lazybones instances = null;

  //私有的構造函數,阻止實例化對象
  private Lazybones(){}

  //若是發現沒有實例對象,就構造一個
  //若是有實例對象,直接返回
  public static Lazybones getInstances(){
    if(instances == null){
      instances = new Lazybones();
    }
    return instances;
  }
}

懶漢式單例模式

小蔡很好奇,問:「代碼也有懶漢?」

老王說:「對啊,在使用到的時候才實例化,這就叫懶漢式。這種方式有個好處就是在不使用的時候不會佔用內存空間。可是這裏也有一個問題。」

小蔡問:「什麼問題啊?」

老王說:「在大併發,多線程的環境下,假若有多個線程同時執行到getInstances()方法,第一個線程執行if語句,尚未完成構造時,第二個線程也執行到if這裏,這時候instance依然爲空。這樣線程一和線程二會同時產生兩個實例。因此懶漢式的單例模式,並非線程安全的 。」

小蔡又問:「難道餓漢式是線程安全的?」

老王說:「對的,餓漢式單例模式的確是線程安全的。我們來看代碼。」

//餓漢式
public class Hungry {
  
  //無論三七二十一,直接實例化一個對象
  private static Hungry instances = new Hungry();

  //私有的構造函數,阻止實例化對象
  private Hungry(){}

  //直接返回已經實例化了的對象
  public static Hungry getInstances(){
    return instances;
  }
}

餓漢式單例模式

老王說:「你看,餓漢式單例模式,無論是否使用,直接就將對象實例化在那兒放着,要用的時候直接使用,這樣就不用再去判斷,因此餓漢式單例模式是線程安全的。」

小蔡問:「老王,咱們怎麼驗證這個這個對象是否單例呢?咱們又看不到內存裏的分配狀況。」

老王說:「這個咱們就得靠對象的哈希碼了。由於哈希碼是用來在散列存儲結構中肯定對象的存儲地址的。簡單理解,就是一個對象的hash code和內存地址是一一對應的,只要hash code值相同,那麼就是同一內存地址。在Java裏,一個對象的toString()方法默認返回這個對象的hash code。咱們來看代碼。」

public class SingleCheck {

  public static void main(String[] args) {
    //經過餓漢式單例模式,得到3個對象
    Hungry h1 = Hungry.getInstances();
    Hungry h2 = Hungry.getInstances();
    Hungry h3 = Hungry.getInstances();
    //打印其Hash code
    System.out.println("h1:" + h1);
    System.out.println("h2:" + h2);
    System.out.println("h3:" + h3);

    //經過懶漢式單例模式,得到3個對象
    Lazybones l1 = Lazybones.getInstances();
    Lazybones l2 = Lazybones.getInstances();
    Lazybones l3 = Lazybones.getInstances();
    //打印其Hash code
    System.out.println("l1:" + l1);
    System.out.println("l2:" + l2);
    System.out.println("l3:" + l3);
  }
}

其結果爲:

運行結果

老王說:「從結果,咱們能夠看到,經過getInstances()方法獲得的對象,不管獲取多少次,的確爲同一對象。這裏我再說一下單例模式的關鍵點:1. 私有的構造函數,防止從外部構造對象。2. 靜態的私有變量存儲對象。3.共開的獲取對象的方法。經過這三點,就能夠完成單例模式的編寫,經過運用單例模式,咱們能夠避免重複構造對象,從而節省系統的內存資源消耗。」

小蔡說:「老王,這就是你說的只有一個寶寶的代碼?」

老王說:「對啊。正是。」

小蔡眨了眨眼,又問:「那若是我不止想要一個寶寶呢?如今國家政策放開了,能夠要兩個寶寶了。」

小蔡想要兩個寶寶

老王說:「那你就得先找老公了呀。」

那你得先找老公

小蔡怒道:「我問的是代碼!只要2個寶寶的代碼!」

我說的是代碼

老王笑道:「那就是多例模式,咱們下回再講。」

更多內容,正在趕來,敬請關注「小蔡和老王的技術平常」。
PS:小蔡和老王的技術平常,已經創建QQ羣,QQ羣號:261434596,歡迎加入。

相關文章
相關標籤/搜索