單例模式,是一種常用的軟件設(shè)計(jì)模式。在它的核心結(jié)構(gòu)中只包含一個(gè)被稱為單例的特殊類。通過單例模式可以保證系統(tǒng)中,應(yīng)用該模式的類一個(gè)類只有一個(gè)實(shí)例。即一個(gè)類只有一個(gè)對象實(shí)例(百度百科)。
單例的實(shí)現(xiàn)有以下幾種方式:
- 非延遲加載,也稱餓漢模式,顧名思義類加載后就實(shí)例化對象。
public class Singleton {
//私有構(gòu)造方法
private Singleton() {}
//Final 實(shí)例
private static final Singleton instance = new Singleton();
//靜態(tài)無參get方法
public static Singleton getInstance() {
return instance;
}
}
- 延遲加載,也稱懶漢模式,等到用到該實(shí)例時(shí)再加載。
public class Singleton {
//私有構(gòu)造方法
private Singleton() {}
//Final 實(shí)例
private static Singleton instance =null;
//靜態(tài)無參get方法
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
synchronized是必須要加的,否則多線程下會(huì)出問題:
線程1先進(jìn)入
getInstance方法,然后在new Singleton()執(zhí)行之前,線程2也進(jìn)入getInstance方法,因?yàn)榫€程1還沒有執(zhí)行new Singleton()操作,固線程2的if(instance == null)判斷也是成立的,因此線程2也new了一個(gè)Singleton對象,這樣單例模式就失效了,因此需要加同步鎖。
- 雙檢查模式
public class Singleton {
//私有構(gòu)造方法
private Singleton() {}
//聲明
private static volatile Singleton instance =null;
//靜態(tài)無參get方法
public static Singleton getInstance() {
if(instance == null) {//1
synchronized (Singleton.class) {
if(instance == null) {//2
instance = new Singleton();//3
}
}
}
return instance;
}
}
Singleton一定要加volatile修飾,否則同樣在多線程下出問題:
線程1先進(jìn)入
getInstance()方法,執(zhí)行到//3處時(shí),線程2進(jìn)入getInstance(),此時(shí)線程1并未初始化完全instance對象(instance = new Singleton()并非是原子操作,第一步為instance分配內(nèi)存,此時(shí)instance已不再為null,第二步調(diào)用其構(gòu)造方法實(shí)例化instance),然而線程2在//1處判斷到instance不為空,則直接返回了未初始化完全的instance對象,故而導(dǎo)致系統(tǒng)崩潰。
- 使用ThreadLocal修復(fù)雙重檢測
public class Singleton {
private static final ThreadLocal<Singleton> threadLocalSingleton = new ThreadLocal<Singleton>();
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
// 每個(gè)線程第一次都會(huì)是null
if (threadLocalSingleton.get() == null) {
createInstance();
}
return singleton;
}
private static final void createInstance() {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
//標(biāo)記當(dāng)前線程已初始化過singleton對象
threadLocalSingleton.set(singleton);
}
}
- 內(nèi)部類模式
參考: 單例模式與雙重檢測
更多關(guān)于volatile的知識,請看另一篇文章Java基礎(chǔ)之volatile