定義:確保某一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。
單例模式的七種實(shí)現(xiàn)方式:
1.餓漢式
public class SingleInstance {
private static SingleInstance mInstance = new SingleInstance();
private SingleInstance() {}
public static SingleInstance getInstance() {
return mInstance;
}
}
2.懶漢式--線程不安全
public class SingleInstance {
private static SingleInstance mInstance;
private SingleInstance() {}
public static SingleInstance getInstance() {
if (mInstance == null) {
mInstance = new SingleInstance();
}
return mInstance;
}
}
3.懶漢式--線程安全
public class SingleInstance {
private static SingleInstance mInstance;
private SingleInstance() {}
public static synchronized SingleInstance getInstance() {
if (mInstance == null) {
mInstance = new SingleInstance();
}
return mInstance;
}
}
優(yōu)點(diǎn):懶漢式的優(yōu)點(diǎn)是只有在使用時(shí)才會(huì)被實(shí)例化,在一定程度上節(jié)約了資源;
缺點(diǎn):每次調(diào)用getInstance()都要同步,造成不必要的同步開(kāi)銷;
4.Double Check Lock(DCL)雙重校驗(yàn)鎖,線程安全
public class SingleInstance {
private static SingleInstance mInstance;
private SingleInstance() {}
public static SingleInstance getInstance() {
if (mInstance == null) {
synchronized (SingleInstance.class) {
if (mInstance == null) {
mInstance = new SingleInstance();
}
}
}
return mInstance;
}
}
在getInstance()方法中對(duì)mInstance進(jìn)行了兩次判空:
第一層判斷主要是為了避免不必要的同步;
第二層判斷是為了在null的情況下創(chuàng)建實(shí)例;
優(yōu)點(diǎn):資源利用率高,第一次執(zhí)行getInstance()的單例對(duì)象才會(huì)被實(shí)例化,效率高;
缺點(diǎn):第一次加載反應(yīng)稍慢,由于Java內(nèi)存模型的原因偶爾會(huì)失敗。在高并發(fā)環(huán)境下也有一定的缺陷,雖然發(fā)生的概率很??;
5.使用靜態(tài)內(nèi)部類的方式
public class SingleInstance {
private SingleInstance() {}
/**
* 靜態(tài)內(nèi)部類
*/
private static class SingleInstanceHolder {
private static SingleInstance mInstance = new SingleInstance();
}
public static SingleInstance getInstance() {
return SingleInstanceHolder.mInstance;
}
}
當(dāng)?shù)谝淮渭虞dSingleInstance類時(shí)不會(huì)初始化mInstance,只有在第一次調(diào)用SingleInstance的getInstance()方法時(shí)才會(huì)導(dǎo)致mInstance被初始化。因此,第一次調(diào)用getInstance()方法會(huì)導(dǎo)致虛擬機(jī)加載SingleInstanceHolder類,這種方式不僅能保證線程安全,也能夠保證單例對(duì)象的唯一性,同時(shí)也延遲了單例的實(shí)例化。
6.枚舉單例
public enum SingleEnum {
INSTANCE;
public void doSomething() {
System.out.println("do somth");
}
}
在上述幾種單例的實(shí)現(xiàn)方式中,如果將對(duì)象實(shí)例進(jìn)行序列化,就會(huì)出現(xiàn)重新創(chuàng)建對(duì)象實(shí)例的情況。而對(duì)應(yīng)枚舉不會(huì)存在這個(gè)問(wèn)題,因?yàn)榧词狗葱蛄谢膊粫?huì)重新生成新的實(shí)例。
7.使用容器實(shí)現(xiàn)單例模式
public class SingleManager {
private static Map<String, Object> objMap = new HashMap<>();
private SingleManager() {}
public static void registerService(String key, Object instance) {
if (!objMap.containsKey(instance)) {
objMap.put(key, instance);
}
}
public static Object getService(String key) {
return objMap.get(key);
}
}
在程序的初始,將多種單例類型注入到一個(gè)統(tǒng)一的管理類中,在使用是根據(jù)key獲取對(duì)象對(duì)應(yīng)的類型的對(duì)象。這中方式使得我們可以管理多種類型的單例,并且在使用時(shí)可以通過(guò)統(tǒng)一的接口進(jìn)行獲取操作,降低了用戶的使用成本,也對(duì)用戶隱藏了具體的實(shí)現(xiàn),降低耦合度。