轉自:http://blog.csdn.net/beyond0525/article/details/22794221
單例模式是設計模式中最常見也最簡單的一種設計模式,保證了在程序中只有一個實例存在並且能全局的訪問到。比如在Android實際APP 開發中用到的 賬號信息對象管理, 數據庫對象(SQLiteOpenHelper)等都會用到單例模式。下面針對一些例子分析一下我們在開發過程中應用單例模式需要注意的點。
一、作用
單例模式(Singleton):保證一個類僅有一個實例,並提供一個訪問它的全局訪問點
二、適用場景
1. 應用中某個實例對象需要頻繁的被訪問。
2. 應用中每次啓動只會存在一個實例。如賬號系統,數據庫系統。
三、常用的使用方式
(1)懶漢式
這是在開發中很容易就能寫出來的一種方式,如下
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
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 在CODE上查看代碼片派生到我的代碼片
Singleton.getInstance().method();
優點:延遲加載(需要的時候纔去加載)
缺點: 線程不安全,在多線程中很容易出現不同步的情況,如在數據庫對象進行的頻繁讀寫操作時。
(2)加同步鎖
既然線程不安全,那就加上同步鎖,一種加法如下:
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
/*2.懶漢式變種,解決線程安全問題**/
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
更一般的寫法是這樣
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
/加上synchronized,但是每次調用實例時都會加載*/
public static Singleton getInstance() {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
調用:
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
Singleton.getInstance().method();
優點:解決了線程不安全的問題。
缺點:效率有點低,每次調用實例都要判斷同步鎖
補充:在Android源碼中使用的該單例方法有:InputMethodManager,AccessibilityManager等都是使用這種單例模式
(3)雙重檢驗鎖
要優化(2)中因爲每次調用實例都要判斷同步鎖的問題,很多人都使用下面的一種雙重判斷校驗的辦法
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
/*3.雙重鎖定:只在第一次初始化的時候加上同步鎖*/
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
這種方法貌似很完美的解決了上述效率的問題,它或許在併發量不多,安全性不太高的情況能完美運行,但是,這種方法也有不幸的地方。問題就是出現在這句
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
instance = new Singleton();
在JVM編譯的過程中會出現指令重排的優化過程,這就會導致當 instance實際上還沒初始化,就可能被分配了內存空間,也就是說會出現 instance !=null 但是又沒初始化的情況,這樣就會導致返回的 instance 不完整(可以參考:http://www.360doc.com/content/11/0810/12/1542811_139352888.shtml)。
調用:
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
Singleton.getInstance().method();
優點:在併發量不多,安全性不高的情況下或許能很完美運行單例模式
缺點:不同平臺編譯過程中可能會存在嚴重安全隱患。
補充:在android圖像開源項目Android-Universal-Image-Loader (https://github.com/nostra13/Android-Universal-Image-Loader)中使用的是這種方式。
(4)內部類的實現
內部類是一種好的實現方式,可以推薦使用一下:
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
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 在CODE上查看代碼片派生到我的代碼片
SingletonInner.getInstance().method();
優點:延遲加載,線程安全(java中class加載時互斥的),也減少了內存消耗
(5)枚舉的方法
這是網上很多人推薦的一種做法,但是貌似使用的不廣泛,大家可以試試,
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
/**
* @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 在CODE上查看代碼片派生到我的代碼片
SingletonEnum.instance.method();
優缺點:如代碼中註釋。
上面主要講了單例模式5種創建方法,大家可以根據其優缺點進行個人實際項目中的使用。
=======================================
上面主要是懶漢式的寫法,其實還有一個是惡漢式的寫法,如下:
/*餓漢式寫法*/
public class Singleton {
/* 持有私有靜態實例,防止被引用 */
private static Singleton instance = new Singleton(); //直接初始化了
/* 私有構造方法,防止被實例化 */
private Singleton() {
}
/* 1:餓漢式 */
public static Singleton getInstance() {
return instance;
}
}
根據使用的頻繁程度可以適當的應用懶漢式或餓漢式,頻率較低的可以用懶漢式,用的時候再創建。