一光酣、簡介:
我們經(jīng)常有這樣的需求: 某一些類應(yīng)該只存在一個(gè)實(shí)例 的時(shí)候,我們就可以用單例模式來應(yīng)對(duì).
單例模式:確保一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)全局訪問點(diǎn).
單例模式是所有設(shè)計(jì)模式中最簡單的一個(gè),也是大部分人最早知道的一個(gè)設(shè)計(jì)模式.
二勾缭、我們經(jīng)常用的2種單例模式(懶漢式揍障、餓漢式)
(1)餓漢式:
餓漢式單例類.在類初始化時(shí),已經(jīng)自行實(shí)例化
public class Singleton {
private Singleton() {}
private static Singleton singleton = new Singleton();
public static Singleton getInstance() {
return singleton ;
}
}
餓漢式在類創(chuàng)建的同時(shí)就已經(jīng)創(chuàng)建好一個(gè)靜態(tài)的對(duì)象供系統(tǒng)使用俩由,以后不再改變毒嫡,所以是線程安全的岩齿。
(2)懶漢式
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public synchronized static Singleton getInstance() {
if (null == singleton) {
singleton= new Singleton();
}
return singleton;
}
}
備注
單例模式的懶漢式實(shí)現(xiàn)方式體現(xiàn)了延遲加載的思想舞吭,什么是延遲加
載呢刨仑? 通俗點(diǎn)說拷泽,就是一開始不要加載
資源或者數(shù)據(jù),一直等咬摇,等到馬上就要使用這個(gè)資源或者數(shù)據(jù)了伐蒂,躲不過去了才加載,所以也稱
Lazy Load肛鹏,不是懶惰啊逸邦,是“延遲加載”,這在實(shí)際開發(fā)中是一種很常見的思想在扰,盡可能的節(jié)約資源缕减。
單例模式的懶漢式實(shí)現(xiàn)還體現(xiàn)了緩存的思想,緩存也是實(shí)際開發(fā)中非常常見的功能芒珠。簡單講就是桥狡,如果
某些資源或者數(shù)據(jù)會(huì)被頻繁的使用,而這些資源或數(shù)據(jù)存儲(chǔ)在系統(tǒng)外部皱卓,比如數(shù)據(jù)庫裹芝、硬盤文件等,
那么每次操作這些數(shù)據(jù)的時(shí)候都從數(shù)據(jù)庫或者硬盤上去獲取好爬,速度會(huì)很慢局雄,會(huì)造成性能問題。 一個(gè)簡單的
解決方法就是:把這些數(shù)據(jù)緩存到內(nèi)存里面存炮,每次操作的時(shí)候,先到內(nèi)存里面找蜈漓,看有沒有這些數(shù)據(jù)穆桂,
如果有,那么就直接使用融虽,如果沒有那么就獲取它享完,并設(shè)置到緩存中,下一次訪問的時(shí)候就可以直接從內(nèi)存
中獲取了有额。從而節(jié)省大量的時(shí)間般又,當(dāng)然,緩存是一種典型的空間換時(shí)間的方案巍佑。
兩種單例模式的比較
比較上面兩種寫法:
1茴迁、懶漢式是典型的時(shí)間換空間,也就是每次獲取實(shí)例都會(huì)進(jìn)行判斷萤衰,看是否需要?jiǎng)?chuàng)建實(shí)例堕义,費(fèi)判斷的時(shí)間,當(dāng)然脆栋,如果一直沒有人使用的話倦卖,那就不會(huì)創(chuàng)建實(shí)例洒擦,節(jié)約內(nèi)存空間。
2怕膛、 餓漢式是典型的空間換時(shí)間熟嫩,當(dāng)類裝載的時(shí)候就會(huì)創(chuàng)建類實(shí)例,不管你用不用褐捻,先創(chuàng)建出來邦危,然后每次調(diào)用的時(shí)候,就不需要再判斷了舍扰,節(jié)省了運(yùn)行時(shí)間倦蚪。
(3)雙重檢查加鎖
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
//先檢查實(shí)例是否存在,如果不存在才進(jìn)入下面的同步塊
if (instance == null) {
//同步塊边苹,線程安全的創(chuàng)建實(shí)例
synchronized (Singleton.class) {
//再次檢查實(shí)例是否存在陵且,如果不存在才真的創(chuàng)建實(shí)例
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
雙重檢查詳解
可以使用“雙重檢查加鎖”的方式來實(shí)現(xiàn),就可以既實(shí)現(xiàn)線程安全个束,又能夠使性能不受到大的影響慕购。
那么什么是“雙重檢查加鎖”機(jī)制呢? 所謂雙重檢查加鎖機(jī)制茬底,指的是:并不是每次進(jìn)入getInstance方法都需要同步沪悲,而是先不同步,進(jìn)入方法過后阱表,先檢查實(shí)例是否存在殿如,如果不存在才進(jìn)入下面的同步塊,這是第一重檢查最爬。進(jìn)入同步塊過后涉馁,再次檢查實(shí)例是否存在,如果不存在爱致,就在同步的情況下創(chuàng)建一個(gè)實(shí)例烤送,這是第二重檢查。這樣一來糠悯,就只需要同步一次了帮坚,從而減少了多次在同步情況下進(jìn)行判斷所浪費(fèi)的時(shí)間。 雙重檢查加鎖機(jī)制的實(shí)現(xiàn)會(huì)使用一個(gè)關(guān)鍵字volatile互艾,它的意思是:被volatile修飾的變量的值试和,將不會(huì)被本地線程緩存,所有對(duì)該變量的讀寫都是直接操作共享內(nèi)存,從而確保多個(gè)線程能正確的處理該變量忘朝。 這種實(shí)現(xiàn)方式既可使實(shí)現(xiàn)線程安全的創(chuàng)建實(shí)例灰署,又不會(huì)對(duì)性能造成太大的影響,它只是在第一次創(chuàng)建實(shí)例的時(shí)候同步,以后就不需要同步了溉箕,從而加快運(yùn)行速度晦墙。 注意:在Java1.4及以前版本中,很多JVM對(duì)于volatile關(guān)鍵字的實(shí)現(xiàn)有問題肴茄,會(huì)導(dǎo)致雙重檢查加鎖的失敗晌畅,因此雙重檢查加鎖的機(jī)制只能用在Java5及以上的版本。
(4)寡痰、靜態(tài)內(nèi)部類單例模式
public class Singleton {
private static class Holder{
private static Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return Holder.INSTANCE;
}
}
當(dāng)?shù)谝淮渭虞dSingleton類時(shí)并不會(huì)初始化INSTANCE抗楔,只有在第一次調(diào)用getInstance方法時(shí)才會(huì)導(dǎo)致INSTANCE被初始化。這種方式不僅能夠保證線程安全拦坠,也能保證單例對(duì)象的唯一性连躏,同時(shí)也延長了單例的實(shí)例化。
小結(jié)
1贞滨、雙重檢查非常適用于高并發(fā),我們熟知的開源庫Eventbus,ImageLoader等都是用的雙重檢查鎖方式實(shí)現(xiàn)單例
2入热、單例模式是運(yùn)用頻率很高的模式,但是晓铆,由于在客戶端通常沒有高并發(fā)的情況勺良,因此,選擇哪種實(shí)現(xiàn)方式都不會(huì)有太大的影響骄噪。即使如此尚困,出于效率考慮,推薦使用DCL單例(雙重檢查鎖定)和靜態(tài)內(nèi)部類單例模式链蕊。