單例模式
1.定義
確保某一個類只有一個實例,而且自行實例化并向整個系統(tǒng)提供這個實例
餓漢式單例模式
public class Singleton {
private static final Singleton singleton = new Singleton();
// 構(gòu)造器私有化,限制產(chǎn)生多個對象
private Singleton() {}
// 對外提供靜態(tài)方法獲取實例對象
public static Singleton getInstence() {
return singleton;
}
}
2.應(yīng)用
2.1優(yōu)點
- 由于單例模式在內(nèi)存中只有一個實例,減小了內(nèi)存開支。特別在一個對象需要頻繁的創(chuàng)建銷毀時,創(chuàng)建銷毀時性能無法優(yōu)化,此時的優(yōu)勢非常明顯
- 由于單例模式只生成一個實例,減少了系統(tǒng)的性能開銷。當一個對象需要產(chǎn)生較多的資源時,如讀取配置,產(chǎn)生其它以來對象,可以通過在應(yīng)用啟動時直接產(chǎn)生一個單例對象,然后永久駐留內(nèi)存的方式解決(JavaEE中采用單例模式時需要注意JVM的垃圾回收機制)
- 單例模式可以避免對同一資源的多重占用,例如寫文件操作
2.2缺點
- 單例模式一般沒有接口,擴展性差
- 單例模式不利于測試。在并行開發(fā)環(huán)境中,若單例模式?jīng)]有完成,不能進行測試
2.3使用場景
- 生成唯一序列號的環(huán)境
- 在整個項目中需要一個共享訪問點或共享數(shù)據(jù)。如一個Web頁面上的計數(shù)器,使用單例模式保持計數(shù)器的值,并確保是線程安全的
- 創(chuàng)建一個對象時需要消耗的資源過多,如訪問IO和數(shù)據(jù)庫等資源
- 需要定義大量的靜態(tài)常量和靜態(tài)方法(如工具類)的環(huán)境
2.4注意事項
在該并發(fā)情況下,需要注意單例模式的線程同步問題。單例模式有幾種不同的實現(xiàn)方式,餓漢式單例模式不會產(chǎn)生多實例的情況,以下方式則需要考慮線程同步問題
懶漢式單例模式
public class Singleton {
private static final Singleton singleton = null;
// 構(gòu)造器私有化,限制產(chǎn)生多個對象
private Singleton() {}
// 對外提供靜態(tài)方法獲取實例對象
public static Singleton getInstence() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
懶漢式單例模式在低并發(fā)情況下尚不會出現(xiàn)問題,若系統(tǒng)壓力增大,并發(fā)量增加時可能在內(nèi)存中存在多個實例。如一個線程A執(zhí)行到singleton = new Singleton(); 但還沒有獲得到對象,此時對象初始化未完成,第二個線程B執(zhí)行到if (singleton == null) { 那么線程B判斷條件也為true ,于是繼續(xù)執(zhí)行下去,則A和B各獲得一個對象。
解決單例模式線程不安全的方法,可以在getInstence 方法前或方法內(nèi)加synchronized 關(guān)鍵字來實現(xiàn),建議采用餓漢式單例模式。
3.擴展
有上限的多例模式:產(chǎn)生固定數(shù)量對象的模式
public class Singleton {
// 定義最多能產(chǎn)生的實例數(shù)量
private static int maxNumOfInstance = 2;
// 定義一個列表容納所有實例
private static List<Singleton> singletonList = new ArrayList<>();
// 定義當前對象實例的序號
private static int countNumOfInstance = 0;
// 產(chǎn)生所有的對象
static {
for (int i = 0; i < maxNumOfInstance; i++) {
singletonList.add(new Singleton())
}
}
private Singleton() {}
public static Singleton getInstence() {
Random random = new Random();
countNumOfInstance = random.nextInt(maxNumOfInstance);
return singletonList.get(countNumOfInstance);
}
}
考慮到線程安全問題可以使用Vector 來代替。采用有上限的多例模式,可以在設(shè)計時決定內(nèi)存中有多少個實例,方便系統(tǒng)擴展,修正單例可能存在的性能問題,提高系統(tǒng)的響應(yīng)速度。如讀取文件,可以在系統(tǒng)啟動時完成初始化工作,在內(nèi)存中啟動固定數(shù)量的Reader 實例。
|