http://blog.csdn.net/beyond0525/article/details/22794221
單例模式是設計模式中最常見也最簡單的一種設計模式,保證了在程序中只有一個實例存在并且能全局的訪問到。比如在Android實際APP 開發中用到的 賬號信息對象管理,數據庫對象(SQLiteOpenHelper)等都會用到單例模式。下面針對一些例子分析一下我們在開發過程中應用單例模式需要注意的點。
一、作用
單例模式(Singleton):保證一個類僅有一個實例,并提供一個訪問它的全局訪問點
二、適用場景
1. 應用中某個實例對象需要頻繁的被訪問。
2. 應用中每次啟動只會存在一個實例。如賬號系統,數據庫系統。
三、常用的使用方式
(1)懶漢式
這是在開發中很容易就能寫出來的一種方式,如下
[java]view plaincopy
publicclassSingleton?{
/*?持有私有靜態實例,防止被引用,此處賦值為null,目的是實現延遲加載?*/
privatestaticSingleton?instance?=null;
/*?私有構造方法,防止被實例化?*/
privateSingleton()?{
}
/*?1:懶漢式,靜態工程方法,創建實例?*/
publicstaticSingleton?getInstance()?{
if(instance?==null)?{
instance?=newSingleton();
}
returninstance;
}
}
調用:
[java]view plaincopy
Singleton.getInstance().method();
優點:延遲加載(需要的時候才去加載)
缺點: 線程不安全,在多線程中很容易出現不同步的情況,如在數據庫對象進行的頻繁讀寫操作時。
(2)加同步鎖
既然線程不安全,那就加上同步鎖,一種加法如下:
[java]view plaincopy
/*2.懶漢式變種,解決線程安全問題**/
publicstaticsynchronizedSingleton?getInstance()?{
if(instance?==null)?{
instance?=newSingleton();
}
returninstance;
}
更一般的寫法是這樣
[java]view plaincopy
/*加上synchronized,但是每次調用實例時都會加載**/
publicstaticSingleton?getInstance()?{
synchronized(Singleton.class)?{
if(instance?==null)?{
instance?=newSingleton();
}
}
returninstance;
}
調用:
[java]view plaincopy
Singleton.getInstance().method();
優點:解決了線程不安全的問題。
缺點:效率有點低,每次調用實例都要判斷同步鎖
補充:在android源碼中使用的該單例方法有:InputMethodManager,AccessibilityManager等都是使用這種單例模式
(3)雙重檢驗鎖
要優化(2)中因為每次調用實例都要判斷同步鎖的問題,很多人都使用下面的一種雙重判斷校驗的辦法
[java]view plaincopy
/*3.雙重鎖定:只在第一次初始化的時候加上同步鎖*/
publicstaticSingleton?getInstance()?{
if(instance?==null)?{
synchronized(Singleton.class)?{
if(instance?==null)?{
instance?=newSingleton();
}
}
}
returninstance;
}
這種方法貌似很完美的解決了上述效率的問題,它或許在并發量不多,安全性不太高的情況能完美運行,但是,這種方法也有不幸的地方。問題就是出現在這句
[java]view plaincopy
instance?=newSingleton();
在JVM編譯的過程中會出現指令重排的優化過程,這就會導致當 instance實際上還沒初始化,就可能被分配了內存空間,也就是說會出現 instance !=null 但是又沒初始化的情況,這樣就會導致返回的?instance 不完整(可以參考:http://www.360doc.com/content/11/0810/12/1542811_139352888.shtml)。
調用:
[java]view plaincopy
Singleton.getInstance().method();
優點:在并發量不多,安全性不高的情況下或許能很完美運行單例模式
缺點:不同平臺編譯過程中可能會存在嚴重安全隱患。
補充:在android圖像開源項目Android-Universal-Image-Loader(https://github.com/nostra13/Android-Universal-Image-Loader)中使用的是這種方式。
(4)內部類的實現
內部類是一種好的實現方式,可以推薦使用一下:
[java]view plaincopy
publicclassSingletonInner?{
/**
*?內部類實現單例模式
*?延遲加載,減少內存開銷
*
*?@author?xuzhaohu
*
*/
privatestaticclassSingletonHolder?{
privatestaticSingletonInner?instance?=newSingletonInner();
}
/**
*?私有的構造函數
*/
privateSingletonInner()?{
}
publicstaticSingletonInner?getInstance()?{
returnSingletonHolder.instance;
}
protectedvoidmethod()?{
System.out.println("SingletonInner");
}
}
調用:
[java]view plaincopy
SingletonInner.getInstance().method();
優點:延遲加載,線程安全(java中class加載時互斥的),也減少了內存消耗
(5)枚舉的方法
這是網上很多人推薦的一種做法,但是貌似使用的不廣泛,大家可以試試,
[java]view plaincopy
/**
*?@function:單例模式枚舉實現
*?@author?xuzhaohu
*
*/
publicenumSingletonEnum?{
/**
*?1.從Java1.5開始支持;
*?2.無償提供序列化機制;
*?3.絕對防止多次實例化,即使在面對復雜的序列化或者反射攻擊的時候;
*/
instance;
privateString?others;
SingletonEnum()?{
}
publicvoidmethod()?{
System.out.println("SingletonEnum");
}
publicString?getOthers()?{
returnothers;
}
publicvoidsetOthers(String?others)?{
this.others?=?others;
}
}
調用:
[java]view plaincopy
SingletonEnum.instance.method();
優缺點:如代碼中注釋。
上面主要講了單例模式5種創建方法,大家可以根據其優缺點進行個人實際項目中的使用。講的屬于拋磚引玉,大家多提意見。