FBI Warning:歡迎轉載,但請標明出處:http://blog.csdn.net/codezjx/article/details/8883599,未經本人同意請勿用於商業用途,感謝支持!
前言:有一些對象其實我們只需要一個,比如說:線程池、緩存、對話框、處理偏好設置和註冊表的對象、日誌對象,充當打印機、顯卡等設備的驅動程序的對象。事實上,這類對象只能有一個實例,如果創造出多個實例,就會導致許多問題產生。例如:程序的行爲異常、資源使用過量,or不一致的結果。Android應用程序開發中,我們也會用到單例。如最經典的:單例SQLiteOpenHelper數據庫操作類,專門處理數據庫之間的交流。
如何實現單例模式?
首先看看單例模式的定義:確保一個類只有一個實例,並提供一個全局訪問點。
一、經典的單例模式實現:
/** 單例*/
private static DBHelper instance = null;
private DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
// TODO Auto-generated constructor stub
}
public static DBHelper getInstance(Context context) {
if(instance == null) {
instance = new DBHelper(context);
}
return instance;
}
注意事項:
1、利用一個靜態變量來記錄DBHelper唯一實例2、構造器必須聲明爲私有的,只有DBHelper類內部纔可以調用構造器
3、getInstance()方法實例化對象,並返回這個實例
4、上面的例子用的是延遲實例化的方式創建,對資源敏感的對象特別重要。
二、多線程下的單例
(1)災難的開始
上面的經典單例實現適合於單線程程序。然而,當引入多線程時,就必須通過同步來保護 getInstance() 方法。如果不保護 getInstance() 方法,則可能返回 DBHelper對象的兩個不同的實例。假設2個線程併發調用 getInstance() 方法並且線程1進入if(instance == null) 後,線程2同時進入if(instance == null)並new了一個對象,此時將存在兩個不同的實例。解決方法:
只要把getInstance() 變成同步(synchronized)方法,多線程的災難幾乎可以輕鬆地解決。代碼如下: public static synchronized DBHelper getInstance(Context context) {
if(instance == null) {
instance = new DBHelper(context);
}
return instance;
}
(2)新的災難
爲了確保我們的程序能應對多線程模式並正常工作,但這又帶來了另外一個災難:同步getInstance() 方法將會極大的拖垮性能。因爲同步一個方法可能造成程序執行效率下降100倍。我們可以進行如下的折衷選擇:
解決方法:
1、若性能對應用程序不是很關鍵,那就忘了上面的性能損失,使用同步getInstance() 方法。若getInstance() 方法運行在很頻繁的地方,就要重新考慮了。2、若資源不敏感,則使用“急切”方法創建實例,而不是延遲實例化的方法,既在靜態初始化器中創建單件,保證線程安全。
3、採用“雙重檢查加鎖(DCL)”,在getInstance() 中減少使用同步,下面將重點介紹。
(3)雙重檢查加鎖(DCL):完美的解決方案
定義:
利用“雙重檢查加鎖”,將首先檢查實例是否已經創建了,如果尚未創建,才進行同步。這樣一來,只有第一次會同步,這就是我們想要的。既避免了多線程災難,又減少了性能損失。看看代碼:
/** 單例*/
private static DBHelper instance = null;
private DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
// TODO Auto-generated constructor stub
}
/** 採用雙重檢查加鎖實例化單件*/
public static DBHelper getInstance(Context context)
{
if(instance == null) //第一次檢查
{
synchronized (DBHelper.class)
{
if(instance == null) //第二次檢查
{
instance = new DBHelper(context);
}
}
}
return instance;
}