1.應(yīng)用場景:
當(dāng)需要保證類在內(nèi)存中的對象唯一性些椒,可以使用單例模式,
不想創(chuàng)建多個實例浪費資源掸刊,或者避免多個實例由于多次調(diào)用
而出現(xiàn)錯誤免糕。一般寫工具類,線程池忧侧,緩存石窑,數(shù)據(jù)庫等可以用到。
2.各種各樣的單例寫法
1.餓漢模式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
獲取單例對象:Singleton instace = Singleton.getInstance();
優(yōu)點:類加載的時候就完成實例化蚓炬,避免線程同步問題
缺點:由于在類加載的時候就進行了實例化松逊,所以沒有達到Lazy Loading(懶加載)
的效果,即使我們沒用到這個實例肯夏,但是他還是會加載经宏,從而造成內(nèi)存浪費(可以忽略犀暑,
最常用的一種)。
2.懶漢模式(線程不安全)
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
盡管達到了懶加載烁兰,但是卻存在線程安全問題耐亏,比如有兩個線程,剛好都
執(zhí)行完if(instance == null)缚柏,接著準(zhǔn)備執(zhí)行instance = new Singleton()
語句苹熏,這樣的結(jié)果會導(dǎo)致我們實例化了兩個Singleton對象,這就是懶漢單例
模式可能會引發(fā)的線程安全問題币喧,解決這個方法轨域,我們可以對getInstance方法加鎖。
3.懶漢模式(線程安全杀餐,但效率低)
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
盡管保證了線程安全干发,但是每個線程在想要獲得實例時,執(zhí)行g(shù)etInstance()
方法都需要進行同步史翘,而實例化代碼只需執(zhí)行一次就夠了枉长,后面獲取該實例,
直接return即可琼讽,方法進行同步效率太低必峰,需要改進。還有一種寫法是:
synchronized (Singleton.class) { instance = new Singleton(); }
這樣一樣是線程不安全的钻蹬,如果你想使用懶漢模式的話吼蚁,推薦使用下面的:
DCL單例(雙重檢查鎖定)。
4.懶漢模式雙重校驗鎖
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(null == instance){
synchronized (Singleton.class) {
if(null == instance){
instance = new Singleton();
}
}
}
return instance;
}
}
代碼中進行了兩次if檢查问欠,這樣就可以保證線程安全肝匆,初始化一次后,
后面再次訪問時顺献,if檢查旗国,直接return 實例化對象。volatile是1.5后
引入的注整,volatile關(guān)鍵字會屏蔽Java虛擬機所做的一些代碼優(yōu)化能曾,會導(dǎo)
致系統(tǒng)運行效率降低,而更好的寫法是使用靜態(tài)內(nèi)部類來實現(xiàn)單例肿轨!
5.靜態(tài)內(nèi)部類實現(xiàn)單例模式
public class Singleton {
private Singleton() {
}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
和餓漢式類似寿冕,兩者都是通過類裝載機制來保證初始化實例
的時候只有一個線程,從而避免線程安全問題萝招,餓漢式的
Singleton類被加載蚂斤,就會實例化,而靜態(tài)內(nèi)部類這種槐沼,
當(dāng)Singleton類被加載時曙蒸,不會立即實例化捌治,調(diào)用getInstance方法,
才會裝載SingletonHolder類纽窟,從而完成Singleton的實例化肖油。
6.枚舉實現(xiàn)單例模式
public enum SingletonEnum {
INSTANCE;
private Singleton instance;
SingletonEnum() {
instance = new Singleton()
}
public Singleton getInstance() {
return instance;
}
}
訪問方式:SingletonEnum.INSTANCE.method();
INSTANCE即為SingletonEnum類型的引用,得到它就可以調(diào)用
枚舉中的方法臂港。既避免了線程安全問題森枪,還能防止反序列化
重新創(chuàng)建新的對象,但是失去了類的一些特性审孽,沒有延時加載县袱,
推薦使用。
7.使用容器實現(xiàn)單例模式
public class SingletonManager {
private static Map<String,Object> objMap = new HashMap<String,Object>();
private Singleton() { }
public static void registerService(String key,Object instance) {
if(!objMap.containsKey(key)) {
objMap.put(key,instance);
}
}
public static Object getService(String key) {
return objMap.get(key);
}
}
將多種單例類型注入到一個統(tǒng)一的管理類中佑力,在使用時根據(jù)key獲取對象
對應(yīng)類型的對象式散。這種方式使得我們可以管理多種類型的單例,并且在使
用時可以通過統(tǒng)一的接口進行獲取操作打颤,降低了用戶的使用成本暴拄,也對用
戶隱藏了具體實現(xiàn),降低了耦合度编饺。