介紹:
單例模式是一種創(chuàng)建型模式。它保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
類圖:

單例模式UML類圖.png
Singleton(單例類):定義一個getInstance操作,允許客戶訪問它的唯一實例,getInstance是一個靜態(tài)方法,主要負責創(chuàng)建自己的唯一實例。
用法:
? 確保某個類有且只有一個對象時。
個人理解:
? 創(chuàng)建一個對象需要消耗過多資源時(IO操作、訪問數(shù)據(jù)庫等)
? 工具類、幫助類( 應用程序的日志應用、接入第三方SDK等)
? 頻繁實例化然后銷毀的對象(日志、網(wǎng)絡(luò)訪問等)
例子:
單例模式是最常用的一個設(shè)計模式,常見的寫法有幾種:餓漢式、懶漢式、懶漢式同步鎖、雙重校驗鎖、靜態(tài)內(nèi)部類的單例模式。
1、餓漢式(線程安全、沒有懶加載)
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
特點:直接在應用加載時初始化,但會浪費內(nèi)存。
解析:在類初始化時已經(jīng)初始化實例,線程安全的。
2、懶漢式(線程不安去,有懶加載)
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
特點:單例的初始化操作,延遲到需要的時候才進行,但線程不安全。
解析:在調(diào)用getInstance()方法時才實例化,達到延遲加載的效果。
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;
}
}
特點:使用同步鎖synchronized保證多線程情況下單例對象的唯一性,但很多不必要的同步會影響性能。
解析:synchronized會造成不必要的同步開銷,很多情況也不需要同步,不推薦使用。
4、雙重校驗鎖(線程安全,有懶加載)
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;
}
}
特點:線程安全,避免了不必要的同步,但高并發(fā)環(huán)境下小概率會有DCL失效問題。
解析:instance = new Singleton();語句,大概做了3件事情:
(1)、給Singleton的實例分配內(nèi)存
(2)、調(diào)用Singleton()的構(gòu)造函數(shù)
(3)、將instance對象指向分配的內(nèi)存空間(instance不是null了)
但由于java編譯器允許處理器亂序執(zhí)行,執(zhí)行順序可能是 (1)-(2)-(3)或者(1)-(3)-(2)。如果是后者情況,切換到另外的線程中,instance已經(jīng)不是null了,線程B直接取走instance,再使用時就會出錯,這就是DCL失效的問題了。
5、靜態(tài)內(nèi)部類的單例模式(線程安全,有懶加載)
public class Singleton{
private Singleton(){}
public static Singleton newInstance(){
return SingletonHolder.instance;
}
//內(nèi)部類,在裝載該內(nèi)部類時才會去創(chuàng)建單例對象
private static class SingletonHolder{
public static Singleton instance = new Singleton();
}
}
特點:懶加載的同時保證線程安全,推薦使用
解析:為什么會線程安全?類的構(gòu)造器<clinit>()方法在多線程環(huán)境中被正確地加載,同步,如果多個線程同時去初始化一個類,那么只有一個線程去執(zhí)行這個類的,其他線程都需要阻塞等待,直到活動線程執(zhí)行<clinit>()方法完畢。
總結(jié):
上述幾種做法已經(jīng)滿足了絕大部分的需求,還有一些其它做法可以參考其它資料哈。
另外,單例模式需要注意內(nèi)存泄漏的問題,當一個對象已經(jīng)不需要再使用本該被回收時,另外一個正在使用的對象持有它的引用從而導致它不能被回收,產(chǎn)生了內(nèi)存泄漏。
感謝您的閱讀~