1. 程式人生 > >Android開發設計模式之——單例模式

Android開發設計模式之——單例模式


單例模式是設計模式中最常見也最簡單的一種設計模式,保證了在程式中只有一個例項存在並且能全域性的訪問到。比如在Android實際APP 開發中用到的 賬號資訊物件管理,資料庫物件(SQLiteOpenHelper)等都會用到單例模式。下面針對一些例子分析一下我們在開發過程中應用單例模式需要注意的點。


一、作用
單例模式(Singleton):保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點

二、適用場景

1. 應用中某個例項物件需要頻繁的被訪問。

2. 應用中每次啟動只會存在一個例項。如賬號系統,資料庫系統。

三、常用的使用方式

(1)懶漢式

這是在開發中很容易就能寫出來的一種方式,如下

[java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. public class Singleton {  
  2.   
  3.     /* 持有私有靜態例項,防止被引用,此處賦值為null,目的是實現延遲載入 */
      
  4.     private static Singleton instance = null;  
  5.   
  6.     /* 私有構造方法,防止被例項化 */  
  7.     private Singleton() {  
  8.     }  
  9.   
  10.     /* 1:懶漢式,靜態工程方法,建立例項 */  
  11.     public static Singleton getInstance() {  
  12.         if (instance == null) {  
  13.             instance = new Singleton();  
  14.         }  
  15.         return instance;  
  16.     }  
  17. }  
public class Singleton {

    /* 持有私有靜態例項,防止被引用,此處賦值為null,目的是實現延遲載入 */
    private static Singleton instance = null;

    /* 私有構造方法,防止被例項化 */
    private Singleton() {
    }

    /* 1:懶漢式,靜態工程方法,建立例項 */
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
呼叫: [java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. Singleton.getInstance().method();  
Singleton.getInstance().method();
優點:延遲載入(需要的時候才去載入)

缺點: 執行緒不安全,在多執行緒中很容易出現不同步的情況,如在資料庫物件進行的頻繁讀寫操作時。

(2)加同步鎖

既然執行緒不安全,那就加上同步鎖,一種加法如下:

[java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. /*2.懶漢式變種,解決執行緒安全問題**/  
  2.  public static synchronized Singleton getInstance() {  
  3.      if (instance == null) {  
  4.          instance = new Singleton();  
  5.      }  
  6.      return instance;  
  7.  }  
   /*2.懶漢式變種,解決執行緒安全問題**/
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
更一般的寫法是這樣 [java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. /*加上synchronized,但是每次呼叫例項時都會載入**/  
  2.  public static Singleton getInstance() {  
  3.      synchronized (Singleton.class) {  
  4.          if (instance == null) {  
  5.              instance = new Singleton();  
  6.          }  
  7.      }  
  8.      return instance;  
  9.  }  
   /*加上synchronized,但是每次呼叫例項時都會載入**/
    public static Singleton getInstance() {
        synchronized (Singleton.class) {
            if (instance == null) {
                instance = new Singleton();
            }
        }
        return instance;
    }
呼叫: [java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. Singleton.getInstance().method();  
Singleton.getInstance().method();
優點:解決了執行緒不安全的問題。

缺點:效率有點低,每次呼叫例項都要判斷同步鎖

補充:在Android原始碼中使用的該單例方法有:InputMethodManager,AccessibilityManager等都是使用這種單例模式

(3)雙重檢驗鎖

要優化(2)中因為每次呼叫例項都要判斷同步鎖的問題,很多人都使用下面的一種雙重判斷校驗的辦法

[java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. /*3.雙重鎖定:只在第一次初始化的時候加上同步鎖*/  
  2.   public static Singleton getInstance() {  
  3.       if (instance == null) {  
  4.           synchronized (Singleton.class) {  
  5.               if (instance == null) {  
  6.                   instance = new Singleton();  
  7.               }  
  8.           }  
  9.       }  
  10.       return instance;  
  11.   }  
  /*3.雙重鎖定:只在第一次初始化的時候加上同步鎖*/
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
這種方法貌似很完美的解決了上述效率的問題,它或許在併發量不多,安全性不太高的情況能完美執行,但是,這種方法也有不幸的地方。問題就是出現在這句 [java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. instance = new Singleton();  
 instance = new Singleton();

在JVM編譯的過程中會出現指令重排的優化過程,這就會導致當 instance實際上還沒初始化,就可能被分配了記憶體空間,也就是說會出現 instance !=null 但是又沒初始化的情況,這樣就會導致返回的 instance 不完整(可以參考:http://www.360doc.com/content/11/0810/12/1542811_139352888.shtml)。

呼叫:

[java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. Singleton.getInstance().method();  
Singleton.getInstance().method();
優點:在併發量不多,安全性不高的情況下或許能很完美執行單例模式

缺點:不同平臺編譯過程中可能會存在嚴重安全隱患。

補充:在android影象開源專案Android-Universal-Image-Loader https://github.com/nostra13/Android-Universal-Image-Loader中使用的是這種方式

(4)內部類的實現

內部類是一種好的實現方式,可以推薦使用一下:

[java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. public class SingletonInner {  
  2.   
  3.     /** 
  4.      * 內部類實現單例模式 
  5.      * 延遲載入,減少記憶體開銷 
  6.      *  
  7.      * @author xuzhaohu 
  8.      *  
  9.      */  
  10.     private static class SingletonHolder {  
  11.         private static SingletonInner instance = new SingletonInner();  
  12.     }  
  13.   
  14.     /** 
  15.      * 私有的建構函式 
  16.      */  
  17.     private SingletonInner() {  
  18.   
  19.     }  
  20.   
  21.     public static SingletonInner getInstance() {  
  22.         return SingletonHolder.instance;  
  23.     }  
  24.   
  25.     protected void method() {  
  26.         System.out.println("SingletonInner");  
  27.     }  
  28. }  
public class SingletonInner {

    /**
     * 內部類實現單例模式
     * 延遲載入,減少記憶體開銷
     * 
     * @author xuzhaohu
     * 
     */
    private static class SingletonHolder {
        private static SingletonInner instance = new SingletonInner();
    }

    /**
     * 私有的建構函式
     */
    private SingletonInner() {

    }

    public static SingletonInner getInstance() {
        return SingletonHolder.instance;
    }

    protected void method() {
        System.out.println("SingletonInner");
    }
}

呼叫:

[java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. SingletonInner.getInstance().method();  
SingletonInner.getInstance().method();
優點:延遲載入,執行緒安全(java中class載入時互斥的),也減少了記憶體消耗

(5)列舉的方法

這是網上很多人推薦的一種做法,但是貌似使用的不廣泛,大家可以試試,

[java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. /** 
  2.  * @function:單例模式列舉實現 
  3.  * @author xuzhaohu 
  4.  *  
  5.  */  
  6. public enum SingletonEnum {  
  7.     /** 
  8.      * 1.從Java1.5開始支援; 
  9.      * 2.無償提供序列化機制; 
  10.      * 3.絕對防止多次例項化,即使在面對複雜的序列化或者反射攻擊的時候; 
  11.      */  
  12.   
  13.     instance;  
  14.   
  15.     private String others;  
  16.   
  17.     SingletonEnum() {  
  18.   
  19.     }  
  20.   
  21.     public void method() {  
  22.         System.out.println("SingletonEnum");  
  23.     }  
  24.   
  25.     public String getOthers() {  
  26.         return others;  
  27.     }  
  28.   
  29.     public void setOthers(String others) {  
  30.         this.others = others;  
  31.     }  
  32. }  
/**
 * @function:單例模式列舉實現
 * @author xuzhaohu
 * 
 */
public enum SingletonEnum {
    /**
     * 1.從Java1.5開始支援;
     * 2.無償提供序列化機制;
     * 3.絕對防止多次例項化,即使在面對複雜的序列化或者反射攻擊的時候;
     */

    instance;

    private String others;

    SingletonEnum() {

    }

    public void method() {
        System.out.println("SingletonEnum");
    }

    public String getOthers() {
        return others;
    }

    public void setOthers(String others) {
        this.others = others;
    }
}

呼叫:

[java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. SingletonEnum.instance.method();  
SingletonEnum.instance.method();
優缺點:如程式碼中註釋。

上面主要講了單例模式5種建立方法,大家可以根據其優缺點進行個人實際專案中的使用。講的屬於拋磚引玉,大家多提意見。


參考:http://zz563143188.iteye.com/blog/1847029

17
0
   

我的同類文章

http://blog.csdn.net

單例模式是設計模式中最常見也最簡單的一種設計模式,保證了在程式中只有一個例項存在並且能全域性的訪問到。比如在Android實際APP 開發中用到的 賬號資訊物件管理,資料庫物件(SQLiteOpenHelper)等都會用到單例模式。下面針對一些例子分析一下我們在開發過程中應用單例模式需要注意的點。


一、作用
單例模式(Singleton):保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點

二、適用場景

1. 應用中某個例項物件需要頻繁的被訪問。

2. 應用中每次啟動只會存在一個例項。如賬號系統,資料庫系統。

三、常用的使用方式

(1)懶漢式

這是在開發中很容易就能寫出來的一種方式,如下

[java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. public class Singleton {  
  2.   
  3.     /* 持有私有靜態例項,防止被引用,此處賦值為null,目的是實現延遲載入 */  
  4.     private static Singleton instance = null;  
  5.   
  6.     /* 私有構造方法,防止被例項化 */  
  7.     private Singleton() {  
  8.     }  
  9.   
  10.     /* 1:懶漢式,靜態工程方法,建立例項 */  
  11.     public static Singleton getInstance() {  
  12.         if (instance == null) {  
  13.             instance = new Singleton();  
  14.         }  
  15.         return instance;  
  16.     }  
  17. }  
public class Singleton {

    /* 持有私有靜態例項,防止被引用,此處賦值為null,目的是實現延遲載入 */
    private static Singleton instance = null;

    /* 私有構造方法,防止被例項化 */
    private Singleton() {
    }

    /* 1:懶漢式,靜態工程方法,建立例項 */
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
呼叫: [java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. Singleton.getInstance().method();  
Singleton.getInstance().method();
優點:延遲載入(需要的時候才去載入)

缺點: 執行緒不安全,在多執行緒中很容易出現不同步的情況,如在資料庫物件進行的頻繁讀寫操作時。

(2)加同步鎖

既然執行緒不安全,那就加上同步鎖,一種加法如下:

[java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. /*2.懶漢式變種,解決執行緒安全問題**/  
  2.  public static synchronized Singleton getInstance() {  
  3.      if (instance == null) {  
  4.          instance = new Singleton();  
  5.      }  
  6.      return instance;  
  7.  }  
   /*2.懶漢式變種,解決執行緒安全問題**/
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
更一般的寫法是這樣 [java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. /*加上synchronized,但是每次呼叫例項時都會載入**/  
  2.  public static Singleton getInstance() {  
  3.      synchronized (Singleton.class) {  
  4.          if (instance == null) {  
  5.              instance = new Singleton();  
  6.          }  
  7.      }  
  8.      return instance;  
  9.  }  
   /*加上synchronized,但是每次呼叫例項時都會載入**/
    public static Singleton getInstance() {
        synchronized (Singleton.class) {
            if (instance == null) {
                instance = new Singleton();
            }
        }
        return instance;
    }
呼叫: [java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. Singleton.getInstance().method();  
Singleton.getInstance().method();
優點:解決了執行緒不安全的問題。

缺點:效率有點低,每次呼叫例項都要判斷同步鎖

補充:在Android原始碼中使用的該單例方法有:InputMethodManager,AccessibilityManager等都是使用這種單例模式

(3)雙重檢驗鎖

要優化(2)中因為每次呼叫例項都要判斷同步鎖的問題,很多人都使用下面的一種雙重判斷校驗的辦法

[java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. /*3.雙重鎖定:只在第一次初始化的時候加上同步鎖*/  
  2.   public static Singleton getInstance() {  
  3.       if (instance == null) {  
  4.           synchronized (Singleton.class) {  
  5.               if (instance == null) {  
  6.                   instance = new Singleton();  
  7.               }  
  8.           }  
  9.       }  
  10.       return instance;  
  11.   }  
  /*3.雙重鎖定:只在第一次初始化的時候加上同步鎖*/
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
這種方法貌似很完美的解決了上述效率的問題,它或許在併發量不多,安全性不太高的情況能完美執行,但是,這種方法也有不幸的地方。問題就是出現在這句 [java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. instance = new Singleton();  
 instance = new Singleton();

在JVM編譯的過程中會出現指令重排的優化過程,這就會導致當 instance實際上還沒初始化,就可能被分配了記憶體空間,也就是說會出現 instance !=null 但是又沒初始化的情況,這樣就會導致返回的 instance 不完整(可以參考:http://www.360doc.com/content/11/0810/12/1542811_139352888.shtml)。

呼叫:

[java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. Singleton.getInstance().method();  
Singleton.getInstance().method();
優點:在併發量不多,安全性不高的情況下或許能很完美執行單例模式

缺點:不同平臺編譯過程中可能會存在嚴重安全隱患。

補充:在android影象開源專案Android-Universal-Image-Loader https://github.com/nostra13/Android-Universal-Image-Loader中使用的是這種方式

(4)內部類的實現

內部類是一種好的實現方式,可以推薦使用一下:

[java] view plain copy print ? 在CODE上檢視程式碼片 派生到我的程式碼片
  1. public class SingletonInner {  
  2.   
  3.     /** 
  4.      * 內部類實現單例模式 
  5.      * 延遲載入,減少記憶體開銷 
  6.      *  
  7.      * @author xuzhaohu 
  8.      *  
  9. &