設計模式系列:單例模式

單例模式

簡介

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

特色

一、單例類只能有一個實例。
二、單例類必須本身建立本身的惟一實例。
三、單例類必須給全部其餘對象提供這一實例github

實現

說到代碼實現,主要是3點
1.私有靜態變量
2.私有化構造函數
3.靜態的對象獲取方法
相信看到這的時候你內心就有個答案了,看看和下面的所謂懶漢模式的代碼是否是同樣。設計模式

懶漢模式,線程不安全

/// <summary>
/// 懶漢,線程不安全
/// </summary>
public class Singleton1
{
     private static Singleton1 _singleton = null;//私有靜態變量

     private Singleton1() { }//私有化構造函數

     public static Singleton1 GetSingleton()//靜態的對象獲取方法
     {
          if (_singleton == null)//保證爲空才建立
          {
               _singleton = new Singleton1();
          }
          return _singleton;
     }
}

若是這個答案和你想的同樣,並一直也是這樣用的,那隻能說too young to simple,此方法其實有倆方面的問題,首先是線程不安全,若是同一時間,多個線程同時獲取實例,則會出現獲取到不一樣的實例,所謂的單例也就成了線程內單例,明顯不對,解決辦法往下看。安全

懶漢模式,線程安全

/// <summary>
/// 懶漢,線程安全
/// </summary>
public class Singleton2
{
     private static Singleton2 _singleton = null;
     private static object _lock = new object();

     private Singleton2() { }

     public static Singleton2 GetSingleton()
     {
          lock (_lock)//保證線程安全
          {
               if (_singleton == null)//保證爲空才建立
               {
                    _singleton = new Singleton2();
               }
               return _singleton;
          }
     }
}

話很少說,看過代碼的你多半你也會說一句,加鎖大法好.到這咱們基本上算是實現了,可是咱們看看這個鎖,雖然解決了多個實例對象問題,可是該方式運行效率卻很低,下一個線程想要獲取對象,就必須等待上一個線程釋放鎖以後,才能夠繼續運行,但實際上對象大部分狀況是已經存在了的並不須要new,所以對上個方法作了個優化。函數

懶漢優化模式(雙檢鎖/雙重校驗鎖(double-checked locking))

/// <summary>
/// 懶漢優化
/// </summary>
public class Singleton3
{
     private static Singleton3 _singleton = null;
     private static object _lock = new object();

     private Singleton3() { }

     public static Singleton3 GetSingleton()
     {
          if (_singleton == null)//保證對象初始化以後,線程不須要等待鎖
          {
               lock (_lock)//保證線程安全
               {
                    if (_singleton == null)//保證爲空才建立
                    {
                         _singleton = new Singleton3();
                    }
               }
          }
          return _singleton;
     }
}

到這咱們想要的單例模式基本算ok了,碼得我本身都累,想一想設計模式這麼多年的沉澱,就沒有簡單點的寫法麼?答案固然是確定有的,從實現方法的命名也能夠看出,除了懶漢模式,還有對應的餓漢模式,相對來講寫法比較簡單。優化

餓漢模式

/// <summary>
/// 餓漢模式1
/// </summary>
public class Singleton4
{
     private static Singleton4 _singleton = null;

     private Singleton4()
     {

     }

     /// <summary>
     /// 靜態構造函數,由CLR調用,在使用以前被調用,並且之調用一次
     /// </summary>
     static Singleton4()
     {
          _singleton = new Singleton4();
     }

     public static Singleton4 GetSingleton()
     {
          return _singleton;
     }
}

/// <summary>
/// 餓漢模式2
/// </summary>
public class Singleton5
{
     /// <summary>
     /// 靜態變量:會在類型第一次使用的時候初始化,並且只初始化一次
     /// </summary>
     private static Singleton5 _singleton = new Singleton5();

     private Singleton5()
     {

     }

     public static Singleton5 GetSingleton()
     {
          return _singleton;
     }  
}

以上倆種方式,我我的認爲是等價的,一個應用了靜態變量,一個用的靜態構造函數。雖然寫法相對簡單,不過,instance 在類裝載時就實例化,雖然致使類裝載的緣由有不少種,在單例模式中大多數都是調用 getInstance 方法, 可是也不能肯定有其餘的方式(或者其餘的靜態方法)致使類裝載,這時候初始化 instance 顯然沒有達到 lazy loading 的效果。嗨,有問題總得解決,看看下面。線程

登記模式(靜態內部類)

public class Singleton6
{
     private static class SingletonHolder
     {
          internal static Singleton6 _singleton = new Singleton6();
     }

     private Singleton6() { }

     public static Singleton6 GetSingleton()
     {
          return SingletonHolder._singleton;
     }
}

這種方式一樣利用了靜態變量的機制來保證初始化 instance 時只有一個線程,同時又解決了lazy loading。由於 SingletonHolder 類沒有被主動使用,只有經過顯式調用 getInstance 方法時,纔會顯式裝載 SingletonHolder 類,從而實例化 instance。想象一下,若是實例化 instance 很消耗資源,因此想讓它延遲加載,另一方面,又不但願在 Singleton 類加載時就實例化,這個時候,這種方式相對來講比較實用。設計

綜述

通常狀況下,懶漢優化模式更多的是讓咱們從頭出發,理解單例模式的運行過程和實現思路,建議使用餓漢方式(Singleton4/Singleton5)。只有在要明確實現 lazy loading 效果時,纔會使用登記模式。code

補充

事實上,經過反射機制是可以實例化構造方法爲private的類的,那基本上會使全部的單例實現失效。因此咱們這裏不考慮反射的狀況
下載源碼對象

相關文章
相關標籤/搜索