單例模式
1.定義
確保某一個類只有一個實例,而且自行實例化并向整個系統(tǒng)提供這個實例
餓漢式單例模式
public class Singleton {
private static final Singleton singleton = new Singleton();
// 構造器私有化训桶,限制產(chǎn)生多個對象
private Singleton() {}
// 對外提供靜態(tài)方法獲取實例對象
public static Singleton getInstence() {
return singleton;
}
}
2.應用
2.1優(yōu)點
- 由于單例模式在內(nèi)存中只有一個實例,減小了內(nèi)存開支。特別在一個對象需要頻繁的創(chuàng)建銷毀時孽文,創(chuàng)建銷毀時性能無法優(yōu)化厌衔,此時的優(yōu)勢非常明顯
- 由于單例模式只生成一個實例璧帝,減少了系統(tǒng)的性能開銷。當一個對象需要產(chǎn)生較多的資源時富寿,如讀取配置睬隶,產(chǎn)生其它以來對象,可以通過在應用啟動時直接產(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;
// 構造器私有化,限制產(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
關鍵字來實現(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
來代替心包。采用有上限的多例模式,可以在設計時決定內(nèi)存中有多少個實例馒铃,方便系統(tǒng)擴展蟹腾,修正單例可能存在的性能問題,提高系統(tǒng)的響應速度区宇。如讀取文件娃殖,可以在系統(tǒng)啟動時完成初始化工作,在內(nèi)存中啟動固定數(shù)量的Reader
實例议谷。