設計模式之單例模式(Singleton)(1)

單例模式是一種比較簡單的設計模式,簡單來講,就是確保一個類只有一個實例,並且自行實例化並向整個系統提供這個實例。java

單例模式特色:數據庫

1)單例類只能有一個實例。設計模式

2)單例類必須本身建立本身的惟一實例。安全

3)單例類必須給全部其餘對象提供這一實例。多線程

類型:建立類模式ide

類圖:函數

clip_image001

圖1 單例模式類圖工具

注:類圖知識點:性能

1.類圖分爲三部分,依次是類名、屬性、方法this

2.以<<開頭和以>>結尾的爲註釋信息

3.修飾符+表明public,-表明private,#表明protected,什麼都沒有表明包可見。

4.帶下劃線的屬性或方法表明是靜態的。

關鍵點:

1)私有的構造方法

2)指向本身實例的私有靜態引用

3)以本身實例爲返回值的靜態的公有的方法

單例模式的優勢:

  • 在內存中只有一個對象,節省內存空間。
  • 避免頻繁的建立銷燬對象,能夠提升性能。
  • 避免對共享資源的多重佔用。
  • 能夠全局訪問。

適用場景:

  • 須要頻繁實例化而後銷燬的對象。
  • 建立對象時耗時過多或者耗資源過多,但又常常用到的對象。
  • 有狀態的工具類對象。
  • 頻繁訪問數據庫或文件的對象。
  • 全部要求只有一個對象的場景。

注意事項:

  • 只能使用單例類提供的方法獲得單例對象,不要使用反射,不然將會實例化一個新對象。
  • 不要作斷開單例類對象與類中靜態引用的危險操做。
  • 多線程使用單例使用共享資源時,注意線程安全問題。

經常使用方式:

根據實例化對象時機的不一樣分爲兩種:

一、餓漢式單例:在單例類被加載時候,就實例化一個對象交給本身的引用;

餓漢式(JAVA)

public class Singleton { private static Singleton singleton = new Singleton(); private Singleton(){} //靜態工廠方法
    public static Singleton getInstance(){ return singleton; } }
View Code

二、懶漢式單例:在調用取得實例方法的時候纔會實例化對象。

懶漢式(JAVA)

public class Singleton { private static Singleton singleton; private Singleton(){} public static synchronized Singleton getInstance(){ if(singleton==null){ singleton = new Singleton(); } return singleton; } }
懶漢式(java)

C#

/// <summary>
/// 單例模式的實現 /// </summary>
public class Singleton { // 定義一個靜態變量來保存類的實例
    private static Singleton uniqueInstance; // 定義私有構造函數,使外界不能建立該類實例
   private Singleton(){ } /// <summary>
   /// 定義公有方法提供一個全局訪問點,同時你也能夠定義公有屬性來提供全局訪問點 /// </summary>
   /// <returns></returns>
   public static Singleton GetInstance() { // 若是類的實例不存在則建立,不然直接返回
        if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }
懶漢式(C#)

若考慮線程安全(一個類或者程序所提供的接口對於線程來講是原子操做,或者多個線程之間的切換不會致使該接口的執行結果存在二義性,也就是說咱們不用考慮同步的問題,那就是線程安全的。)問題,可對getInstance()方法進行改造,有下列三種方式:

1)在getInstance方法上加同步

JAVA

public static synchronized Singleton getInstance() { if (single == null) { single = new Singleton(); } return single; } 
View Code

C#

/// <summary>
/// 單例模式的實現 /// </summary>
public class Singleton { // 定義一個靜態變量來保存類的實例
    private static Singleton uniqueInstance; // 定義一個標識確保線程同步
    private static readonly object locker = new object(); // 定義私有構造函數,使外界不能建立該類實例
    private Singleton() { } /// <summary>
    /// 定義公有方法提供一個全局訪問點,同時你也能夠定義公有屬性來提供全局訪問點 /// </summary>
    /// <returns></returns>
    public static Singleton GetInstance() { // 當第一個線程運行到這裏時,此時會對locker對象 "加鎖", // 當第二個線程運行該方法時,首先檢測到locker對象爲"加鎖"狀態,該線程就會掛起等待第一個線程解鎖 // lock語句運行完以後(即線程運行完以後)會對該對象"解鎖"
        lock (locker) { // 若是類的實例不存在則建立,不然直接返回
            if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } return uniqueInstance; } }
View Code

 

2)雙重檢查鎖定

JAVA

public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; }
View Code

C#

/// <summary>
/// 單例模式的實現 /// </summary>
public class Singleton { // 定義一個靜態變量來保存類的實例
    private static Singleton uniqueInstance; // 定義一個標識確保線程同步
    private static readonly object locker = new object(); // 定義私有構造函數,使外界不能建立該類實例
    private Singleton() { } /// <summary>
    /// 定義公有方法提供一個全局訪問點,同時你也能夠定義公有屬性來提供全局訪問點 /// </summary>
    /// <returns></returns>
    public static Singleton GetInstance() { // 當第一個線程運行到這裏時,此時會對locker對象 "加鎖", // 當第二個線程運行該方法時,首先檢測到locker對象爲"加鎖"狀態,該線程就會掛起等待第一個線程解鎖 // lock語句運行完以後(即線程運行完以後)會對該對象"解鎖" // 雙重鎖定只須要一句判斷就能夠了
        if (uniqueInstance == null) { lock (locker) { // 若是類的實例不存在則建立,不然直接返回
                if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } } return uniqueInstance; } }
View Code

3)靜態內部類

JAVA

public class Singleton { private static class LazyHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return LazyHolder.INSTANCE; } } 
View Code

以上三種方式實現區別:

第1種,在方法調用上加了同步,雖然線程安全了,可是每次都要同步,會影響性能,畢竟99%的狀況下是不須要同步的;

第2種,在getInstance中作了兩次null檢查,確保了只有第一次調用單例的時候纔會作同步,這樣也是線程安全的,同時避免了每次都同步的性能損耗;

第3種,利用了classloader的機制(雙親委託模型)來保證初始化instance時只有一個線程,因此也是線程安全的,同時沒有性能損耗,因此通常建議使用這一種。

餓漢式和懶漢式區別:

一、線程安全:

餓漢式天生就是線程安全的,能夠直接用於多線程而不會出現問題;懶漢式自己是非線程安全的,爲了實現線程安全有幾種寫法,分別是上面的一、二、3,這三種實如今資源加載和性能方面有些區別。

二、資源加載和性能:

餓漢式在類建立的同時就實例化一個靜態對象出來,無論以後會不會使用這個單例,都會佔據必定的內存,可是相應的,在第一次調用時速度也會更快,由於其資源已經初始化完成,而懶漢式顧名思義,會延遲加載,在第一次使用該單例的時候纔會實例化對象出來,第一次調用時要作初始化,若是要作的工做比較多,性能上會有些延遲,以後就和餓漢式同樣了。

在java中,餓漢式單例要優於懶漢式單例。C#中則通常使用懶漢式單例。

懶漢式雙重檢查鎖定方式舉例:

JAVA:

package Demo; //懶漢式
public class Test { String input = null; private Test() { } private static volatile Test instance = null; //雙重檢查鎖定的方式
    public static Test getInstance() { if (instance == null) { synchronized (Test.class) { if (instance == null) { instance = new Test(); } } } return instance; } public String getInput() { return input; } public void setInput(String input) { this.input = input; } public void printInfo() { System.out.println(input); } } package Demo; public class SingletonDemo { public static void main(String[] args) { // TODO Auto-generated method stub
        Test a = Test.getInstance(); a.setInput("chen"); Test b = Test.getInstance(); b.setInput("cll"); a.printInfo(); b.printInfo(); if(a == b){ System.out.println("同一個實例"); }else{ System.out.println("不是同一個實例"); } } }
View Code

輸出結果:

clip_image003

C#

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SingletonDemo { public class Test { String input = null; private Test() { } private static  Test instance = null; // 定義一個標識確保線程同步
    private static readonly object locker = new object(); //雙重檢查鎖定的方式
    public static Test getInstance() { if (instance == null) { lock (locker) { if (instance == null) { instance = new Test(); } } } return instance; } public String getInput() { return input; } public void setInput(String input) { this.input = input; } public void printInfo() { Console.WriteLine(input); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SingletonDemo { class Program { static void Main(string[] args) { Test a = Test.getInstance(); a.setInput("chen"); Test b = Test.getInstance(); b.setInput("cll"); a.printInfo(); b.printInfo(); if(a == b){ Console.WriteLine("同一個實例"); }else{ Console.WriteLine("不是同一個實例"); } } } }
View Code

輸出結果:

clip_image005

結論:由輸出結果能夠得知單例模式爲一個面向對象的應用程序提供了對象唯一的訪問點,無論它實現何種功能,整個應用程序都會共享一個實例對象。

相關文章
相關標籤/搜索