單例模式(Singleton Pattern)是 Java 中最簡(jiǎn)單的設(shè)計(jì)模式之一。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式秀存,它提供了一種創(chuàng)建對(duì)象的最佳方式捶码。
這種模式涉及到一個(gè)單一的類,該類負(fù)責(zé)創(chuàng)建自己的對(duì)象或链,同時(shí)確保只有單個(gè)對(duì)象被創(chuàng)建宙项。這個(gè)類提供了一種訪問其唯一的對(duì)象的方式,可以直接訪問株扛,不需要實(shí)例化該類的對(duì)象尤筐。
創(chuàng)建單例模式的方法有很多種,這里由淺顯地講解一下常用的實(shí)現(xiàn)單例模式的三種方法洞就。
餓漢式單例模式
/**
* 餓漢式單例模式盆繁,初始化時(shí)即生成
*/
public class Singleton1 {
// 私有化構(gòu)造方法使得該類無法在外部通過 new 進(jìn)行實(shí)例化
private Singleton1(){
}
// 準(zhǔn)備一個(gè)類屬性(靜態(tài)!Q油昂!),指向一個(gè)實(shí)例化對(duì)象倾贰。 因?yàn)槭穷悓傩悦岬允菃卫摹? private static Singleton1 instance = new Singleton1();
// public static 方法,提供給調(diào)用者獲取定義的對(duì)象
public static Singleton1 getInstance(){
return instance;
}
}
懶漢式單例模式(lazy機(jī)制)
/**
* 懶漢式單例模式(lazy機(jī)制匆浙,線程安全安寺,但效率低)
*/
public class Singleton2 {
// 私有化構(gòu)造方法使得該類無法在外部通過 new 進(jìn)行實(shí)例化
private Singleton2() {
}
// 聲明為 private 和 static 表明該類只能在該Singleton類中被訪問且只能被創(chuàng)建一次,實(shí)現(xiàn)了lazy機(jī)制
private static class SingletonHolder {
private static final Singleton2 instance = new Singleton2();
}
// 當(dāng) Singleton 類加載時(shí)首尼,靜態(tài)內(nèi)部類 SingletonHolder 沒有被加載進(jìn)內(nèi)存挑庶。
// 只有當(dāng)調(diào)用 getUniqueInstance() 方法從而觸發(fā) SingletonHolder.INSTANCE 時(shí) SingletonHolder 才會(huì)被加載,
// 此時(shí)初始化 INSTANCE 實(shí)例软能,并且 JVM 能確保 INSTANCE 只被實(shí)例化一次迎捺。
public static Singleton2 getInstance() {
return SingletonHolder.instance;
}
}
雙重校驗(yàn)鎖(DCL)實(shí)現(xiàn)單例模式(并發(fā)環(huán)境下的單例實(shí)現(xiàn)方式:線程安全)
/**
* DCL實(shí)現(xiàn)單例模式(并發(fā)環(huán)境下的單例實(shí)現(xiàn)方式)
*/
public class Singleton3 {
// 加入volatile保證可見性和原子性,保證JVM不會(huì)對(duì)指令進(jìn)行重排序查排。
private volatile static Singleton3 instance;
private Singleton3() {}
public static Singleton3 getInstance() {
// 第一次判斷凳枝,避免在instance實(shí)例已經(jīng)創(chuàng)建的情況下進(jìn)入synchronized同步代碼塊,提升效率
if (instance == null) {
synchronized (Singleton3.class) {
// 第二次判斷跋核,為了避免其他線程在加鎖的空隙期間已創(chuàng)建過實(shí)例
if (instance == null) {
instance = new Singleton3();
}
}
}
return instance;
}
}
使用volatile關(guān)鍵字是很有必要的岖瑰,因?yàn)閯?chuàng)建對(duì)象實(shí)際上是分三步進(jìn)行。對(duì)于instance = new Singleton3() 這句代碼來說了罪,分三步進(jìn)行:
- 為instance分配內(nèi)存空間
- 初始化instance
- 將instance指向分配的內(nèi)存地址
如果不使用volatile關(guān)鍵字锭环,由于JVM可能會(huì)進(jìn)行指令重排,執(zhí)行順序可能會(huì)變泊藕。指令重排在單線程下不會(huì)出現(xiàn)問題辅辩,但是在多線程下就有可能會(huì)讓一個(gè)線程獲取到未被初始化的對(duì)象實(shí)例,這樣顯然就出現(xiàn)了問題娃圆。