介紹:
單例模式是一種創(chuàng)建型模式碴开。它保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)魏宽。
類圖:
單例模式UML類圖.png
Singleton(單例類):定義一個(gè)getInstance操作塔插,允許客戶訪問它的唯一實(shí)例桐经,getInstance是一個(gè)靜態(tài)方法,主要負(fù)責(zé)創(chuàng)建自己的唯一實(shí)例主到。
用法:
? 確保某個(gè)類有且只有一個(gè)對(duì)象時(shí)茶行。
個(gè)人理解:
? 創(chuàng)建一個(gè)對(duì)象需要消耗過多資源時(shí)(IO操作、訪問數(shù)據(jù)庫(kù)等)
? 工具類登钥、幫助類( 應(yīng)用程序的日志應(yīng)用畔师、接入第三方SDK等)
? 頻繁實(shí)例化然后銷毀的對(duì)象(日志、網(wǎng)絡(luò)訪問等)
例子:
單例模式是最常用的一個(gè)設(shè)計(jì)模式牧牢,常見的寫法有幾種:餓漢式茉唉、懶漢式固蛾、懶漢式同步鎖、雙重校驗(yàn)鎖度陆、靜態(tài)內(nèi)部類的單例模式艾凯。
1、餓漢式(線程安全懂傀、沒有懶加載)
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
特點(diǎn):直接在應(yīng)用加載時(shí)初始化趾诗,但會(huì)浪費(fèi)內(nèi)存。
解析:在類初始化時(shí)已經(jīng)初始化實(shí)例蹬蚁,線程安全的恃泪。
2、懶漢式(線程不安去犀斋,有懶加載)
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
特點(diǎn):單例的初始化操作贝乎,延遲到需要的時(shí)候才進(jìn)行,但線程不安全叽粹。
解析:在調(diào)用getInstance()方法時(shí)才實(shí)例化览效,達(dá)到延遲加載的效果。
3虫几、懶漢式同步鎖(線程安全锤灿,有懶加載)
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
}
特點(diǎn):使用同步鎖synchronized保證多線程情況下單例對(duì)象的唯一性,但很多不必要的同步會(huì)影響性能辆脸。
解析:synchronized會(huì)造成不必要的同步開銷但校,很多情況也不需要同步,不推薦使用啡氢。
4状囱、雙重校驗(yàn)鎖(線程安全,有懶加載)
public class Singleton {
private static volatile Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
if (instance == null) { //此處避免了不必要的同步
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
特點(diǎn):線程安全倘是,避免了不必要的同步浪箭,但高并發(fā)環(huán)境下小概率會(huì)有DCL失效問題。
解析:instance = new Singleton();
語(yǔ)句辨绊,大概做了3件事情:
(1)奶栖、給Singleton的實(shí)例分配內(nèi)存
(2)、調(diào)用Singleton()的構(gòu)造函數(shù)
(3)门坷、將instance對(duì)象指向分配的內(nèi)存空間(instance不是null了)
但由于java編譯器允許處理器亂序執(zhí)行宣鄙,執(zhí)行順序可能是 (1)-(2)-(3)或者(1)-(3)-(2)。如果是后者情況默蚌,切換到另外的線程中冻晤,instance已經(jīng)不是null了,線程B直接取走instance绸吸,再使用時(shí)就會(huì)出錯(cuò)鼻弧,這就是DCL失效的問題了设江。
5、靜態(tài)內(nèi)部類的單例模式(線程安全攘轩,有懶加載)
public class Singleton{
private Singleton(){}
public static Singleton newInstance(){
return SingletonHolder.instance;
}
//內(nèi)部類叉存,在裝載該內(nèi)部類時(shí)才會(huì)去創(chuàng)建單例對(duì)象
private static class SingletonHolder{
public static Singleton instance = new Singleton();
}
}
特點(diǎn):懶加載的同時(shí)保證線程安全,推薦使用
解析:為什么會(huì)線程安全度帮?類的構(gòu)造器<clinit>()方法在多線程環(huán)境中被正確地加載歼捏,同步,如果多個(gè)線程同時(shí)去初始化一個(gè)類笨篷,那么只有一個(gè)線程去執(zhí)行這個(gè)類的瞳秽,其他線程都需要阻塞等待,直到活動(dòng)線程執(zhí)行<clinit>()方法完畢率翅。
總結(jié):
上述幾種做法已經(jīng)滿足了絕大部分的需求练俐,還有一些其它做法可以參考其它資料哈。
另外冕臭,單例模式需要注意內(nèi)存泄漏的問題腺晾,當(dāng)一個(gè)對(duì)象已經(jīng)不需要再使用本該被回收時(shí),另外一個(gè)正在使用的對(duì)象持有它的引用從而導(dǎo)致它不能被回收浴韭,產(chǎn)生了內(nèi)存泄漏。
感謝您的閱讀~