單例模式的幾種寫(xiě)法
1才沧、餓漢模式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){
}
public static Singleton getInstance() {
return instance;
}
}
這種寫(xiě)法是在類裝載時(shí)就實(shí)例化instance缘挑,他避免了多線程的同步問(wèn)題集歇。但是不能保證有別的方式去裝載,沒(méi)有達(dá)到懶加載语淘。
2诲宇、懶漢模式(線程不安全)
public class Singleton {
private static Singleton instance;
private Singleton (){
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
達(dá)到了懶加載,但是在多線程不能正常工作惶翻。
3姑蓝、懶漢模式(線程安全)
public class Singleton {
private static Singleton instance;
private Singleton (){
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
這種寫(xiě)法能夠在多線程中很好的工作,但是每次調(diào)用getInstance方法都會(huì)進(jìn)行同步吕粗,反應(yīng)稍慢侥猩,還會(huì)造成不必要的開(kāi)銷瘟则,所以者這種不建議使用。
4、DCL單例(雙重檢查鎖定)
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){
}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
這種寫(xiě)法在getSingleton方法中對(duì)singleton進(jìn)行了兩次判空淡诗,第一次是為了不必要的同步,第二次是為了在null的情況下創(chuàng)建實(shí)例驮配。我們會(huì)發(fā)現(xiàn)上面代碼有一個(gè)volatile關(guān)鍵字鬓催,因?yàn)樵谶@里會(huì)有DCL失效問(wèn)題,原因是Java編譯器允許處理器亂序執(zhí)行先口。那么為了解決這個(gè)問(wèn)題型奥,在JDK1.5之后,具體化了volatile關(guān)鍵字碉京,只要定義時(shí)加上他厢汹,可以保證執(zhí)行的順序,雖然會(huì)影響性能谐宙。這種方式第一次加載時(shí)會(huì)稍慢烫葬,在高并發(fā)環(huán)境會(huì)有缺陷,但是一般能夠滿足需求卧惜。
5厘灼、靜態(tài)內(nèi)部類單例模式
public class Singleton {
private Singleton (){
}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
/**
*靜態(tài)內(nèi)部類
*/
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
這種是推薦使用的單例模式實(shí)現(xiàn)方式夹纫。當(dāng)?shù)谝淮渭虞dSingleton類時(shí)并不會(huì)初始化INSTANCE,只有在第一次調(diào)用getInstance方法時(shí)才會(huì)導(dǎo)致INSTANCE被初始化设凹。這種方式不僅能夠保證線程安全舰讹,也能保證單例對(duì)象的唯一性,同時(shí)也延長(zhǎng)了單例的實(shí)例化闪朱。
6月匣、枚舉單例
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
這種方式是Effective Java作者Josh Bloch 提倡的方式,它不僅能避免多線程同步問(wèn)題奋姿,而且還能防止反序列化重新創(chuàng)建新的對(duì)象锄开。
Android源碼中的單例模式
在Android系統(tǒng)中,我們經(jīng)常會(huì)通過(guò)Context獲取系統(tǒng)級(jí)別的服務(wù)称诗,如WindowsManagerService萍悴、ActivityManagerService等,更常用的是一個(gè)LayoutInflater的類寓免,這些服務(wù)會(huì)在合適的時(shí)候以單例的形式注冊(cè)在系統(tǒng)中癣诱,在我們需要的時(shí)候就通過(guò)Context的getSystemService(String name)獲取。
總結(jié)
優(yōu)點(diǎn):
(1)由于單例模式在內(nèi)存中只有一個(gè)實(shí)例袜香,減少了內(nèi)存開(kāi)支撕予,特別是一個(gè)對(duì)象需要頻繁的創(chuàng)建、銷毀時(shí)蜈首,而且創(chuàng)建或銷毀時(shí)性能又無(wú)法優(yōu)化实抡,單例模式的優(yōu)勢(shì)就非常明顯。
(2)單例模式可以避免對(duì)資源的多重占用欢策,例如一個(gè)文件操作吆寨,由于只有一個(gè)實(shí)例存在內(nèi)存中,避免對(duì)同一資源文件的同時(shí)操作踩寇。
(3)單例模式可以在系統(tǒng)設(shè)置全局的訪問(wèn)點(diǎn)鸟废,優(yōu)化和共享資源訪問(wèn),例如姑荷,可以設(shè)計(jì)一個(gè)單例類,負(fù)責(zé)所有數(shù)據(jù)表的映射處理缩擂。
缺點(diǎn):
(1)單例模式違背了面向?qū)ο箝_(kāi)閉原則鼠冕,不能通過(guò)接口擴(kuò)展,若要擴(kuò)展胯盯,只能修改代碼來(lái)實(shí)現(xiàn)懈费。
(2)單例對(duì)象如果持有Context,那么很容易引發(fā)內(nèi)存泄露博脑。此時(shí)需要注意傳遞給單例對(duì)象的Context最好是Application Context憎乙。