歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 設計模式之單例模式

設計模式之單例模式

日期:2017/3/1 9:27:09   编辑:Linux編程

方法的提煉和重要,同樣的代碼加入出現了5遍,只要我們稍微一修改,我們要修改的就是5個地方,可能我們有時候改了幾個地方,有1、2個地方漏改了,方法的提煉有助於開發的維護和擴展。好吧,扯遠了,回到我們的正題,單例模式。

一、定義:

單例模式(Singleton),保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

通常我們可以讓一個全局變量使得一個對象被訪問,但它不能防止你的實例化多個對象,一個最好的方法就是,讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例可以被創建,並且它可以提供一個訪問該實例的方法。    通俗的說就是, 將構造函數聲明為private,防止外部類的實例化,提供一個public的方法用於生成對象。

二、UML圖

名詞解釋:

lazy Loading:延遲加載,即在需要的時候載加載類,而不是一開始就加載。

三、常用方法形式

方式1、將構造函數聲明為private,提供一個公共的外界調用該對象的方式(lazy Loading,線程不安全)


public class Singleton{
private static Singletoninstance=null;
private Singleton(){ //構造方法是private,堵死了外界用new實例化
//dosomething
}
//獲取本類實例的唯一全局訪問點
public static Singleton getInstance(){
if(instance==null){ //若實例不存在,創建一個返回,否則返回已有實例
instance=new Singleton();
}
return instance;
}
}

  主要缺點:在線程下工作容易出問題,生成多個對象。

方式2、(lazy Loading,線程不安全)


public class Singleton{
private static Singletoninstance=null;
private Singleton(){ //構造方法是private,堵死了外界用new實例化
//dosomething
}
//獲取本類實例的唯一全局訪問點
public static synchronized Singleton getInstance(){
if(instance==null){ //若實例不存在,創建一個返回,否則返回已有實例
instance=new Singleton();
}
return instance;
}
}

  能很好的解決上面說的多線程問題,但是使多線程的變成了單線程的效率,效率很差。一般不適用。

方式3、在類裡面生成唯一的對象,每次生成該對象都是返回那唯一的對象。(非Lazy Loading)


public class Singleton{
//在自己內部定義自己的一個實例,只供內部調用
private static Singleton instance=new Singleton();
private Singleton(){
//dosomething
}
//這裡提供了一個供外部訪問本class的靜態方法,可以直接訪問
public static Singleton getInstance(){
return instance;
}
}

  不是延遲加載類型,類在類加載的時候就進行了初始化,這樣子會浪費一定的知源,違背了延遲加載的性質。

方式4

  


public class Singleton{
private static Singletoninstance=null;
private Singleton(){
//dosomething
}
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
if(null==instance){
instance=new Singleton();
}
}
}
return instance;
}
}
//這個模式將同步內容下方到if內部,提高了執行的效率,不必每次
獲取對象時都進行同步,只有第一次才同步,創建了以後就沒必要了。

 通過靜態代碼塊(static)的方式,將對象生成延遲了類初始化的時候。

方式5、雙重鎖機制


  public class Singleton
  {
  //定義一個私有的靜態全局變量來保存該類的唯一實例
  private static Singleton singleton;
//定義一個只讀靜態對象
  //且這個對象是在程序運行時創建的
  private static readonly object syncObject = new object();
/// <summary>
  /// 構造函數必須是私有的
  /// 這樣在外部便無法使用 new 來創建該類的實例
  /// </summary>
  private Singleton()
  {}
/// <summary>
  /// 定義一個全局訪問點
  /// 設置為靜態方法
  /// 則在類的外部便無需實例化就可以調用該方法
  /// </summary>
  /// <returns></returns>
  public static Singleton GetInstance()
  {
  //這裡可以保證只實例化一次
  //即在第一次調用時實例化
  //以後調用便不會再實例化
//第一重 singleton == null
  if (singleton == null)
  {
lock (syncObject)
  {
  //第二重 singleton == null
  if (singleton == null)
  {
  singleton = new Singleton();
  }
  }
  }
  return singleton;
  }
  }
  

解析:在外層已經判斷了singleton == null,為什麼在lock()後面還需要判斷一次singleton == null呢?

因為,加入有兩個線程同時調用了getInstance()方法,它們將都可以通過第一重singleton == null的判斷,然後由於lock機智,這兩個線程則一個進入,另一個在排隊等待,必須要其中的一個進入並出來後,另外一個才能進入,因此如果沒有了第二重instance是否為空的判斷,則第一個線程創建了實例,而第二個線程還是可以繼續再創建新的實例,這就沒有達到單例的目的了。

Copyright © Linux教程網 All Rights Reserved