前言
本文主要給大家講解多線程的一個(gè)重要案例 — 單例模式.
關(guān)注收藏, 開(kāi)始學(xué)習(xí)吧??
1. 什么是單例模式
單例模式是一種很經(jīng)典的設(shè)計(jì)模式, 那么什么叫做設(shè)計(jì)模式呢?
設(shè)計(jì)模式好比象棋中的 “棋譜”.
紅方當(dāng)頭炮, 黑方馬來(lái)跳. 針對(duì)紅方的一些走法, 黑方應(yīng)招的時(shí)候有一些固定的套路. 按照套路來(lái)走局勢(shì)就不會(huì)吃虧.
軟件開(kāi)發(fā)中也有很多常見(jiàn)的 “問(wèn)題場(chǎng)景”. 針對(duì)這些問(wèn)題場(chǎng)景, 大佬們總結(jié)出了一些固定的套路. 按照這個(gè)套路來(lái)實(shí)現(xiàn)代碼, 也不會(huì)吃虧.
單例模式能保證某個(gè)類(lèi)在程序中只存在唯一一份實(shí)例, 而不會(huì)創(chuàng)建出多個(gè)實(shí)例.
這一點(diǎn)在很多場(chǎng)景上都需要. 比如 JDBC 中的 DataSource 實(shí)例就只需要一個(gè).
單例模式具體的實(shí)現(xiàn)方式有很多種寫(xiě)法, 在這里我們主要講解 “餓漢” 和 “懶漢” 兩種.
2. 餓漢模式
核心思想: 類(lèi)加載的同時(shí), 創(chuàng)建實(shí)例.
class Singleton {
private static Singleton instanse = new Singleton();
public static Singleton getInstance() {
return instanse;
}
private Singleton() {};
}
注意:
-
private static Singleton instanse = new Singleton();
被 static 修飾, 該屬性是類(lèi)的屬性, JVM 中, 每個(gè)類(lèi)的類(lèi)對(duì)象只有唯一一份, 類(lèi)對(duì)象里的這個(gè)成員自然也是唯一一份了. -
private Singleton() {};
將構(gòu)造方法設(shè)為 private, 就可以將外部的 new 操作給禁用掉. - 此時(shí), 在類(lèi)內(nèi)部把實(shí)例創(chuàng)建好, 同時(shí)禁止外部重新創(chuàng)造實(shí)例, 就可以保證單例的特性了.
由于構(gòu)造方法設(shè)為 private, 導(dǎo)致 new 操作被禁用, 我們只能通過(guò)類(lèi)方法 .getInstanse
來(lái)創(chuàng)建實(shí)例, 可以看到, 這樣先后創(chuàng)建的 s1 和 s2 實(shí)例, 其實(shí)是同一個(gè)實(shí)例.
3. 懶漢模式 — 單線程版
核心思想: 類(lèi)加載的時(shí)候不創(chuàng)建實(shí)例. 第一次使用的時(shí)候才創(chuàng)建實(shí)例.
class SingletonLazy {
private static SingletonLazy instance = null;
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
private SingletonLazy() {};
}
注意點(diǎn)與餓漢模式差不多, 但是區(qū)別在于, 懶漢模式是 “非必要不創(chuàng)建”, 可以看到, instance 實(shí)例對(duì)象是在調(diào)用類(lèi)方法時(shí)才創(chuàng)建的.
4. 懶漢模式 — 多線程版
現(xiàn)在問(wèn)題來(lái)了, 上述兩個(gè)模式, 是否能構(gòu)保證線程安全呢?
多個(gè)線程下調(diào)用 getInstance 方法, 是否會(huì)出現(xiàn)問(wèn)題呢?
回想一下我們之前講解的線程不安全的幾個(gè)原因. 可以推斷餓漢模式下, 線程是安全的, 因?yàn)樗皇亲x數(shù)據(jù), 并沒(méi)有進(jìn)行寫(xiě)數(shù)據(jù).
但是多線程下, 懶漢模式可能無(wú)法保證創(chuàng)建對(duì)象的唯一性, 線程不安全.
線程安全問(wèn)題發(fā)生在首次創(chuàng)建實(shí)例時(shí). 如果在多個(gè)線程中同時(shí)調(diào)用 getInstance 方法, 就可能導(dǎo)致創(chuàng)建出多個(gè)實(shí)例.
一旦實(shí)例已經(jīng)創(chuàng)建好了, 后面再多線程環(huán)境調(diào)用 getInstance 就不再有線程安全問(wèn)題了(不再修改instance 了)
我們可以通過(guò)加鎖, 利用 synchronized 關(guān)鍵字就可以改善這里的線程安全問(wèn)題.
class SingletonLazy {
private static SingletonLazy instance = null;
public synchronized static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
private SingletonLazy() {};
}
5. 懶漢模式 — 多線程改進(jìn)版
加鎖其實(shí)是一個(gè)比較低效的操作, 因?yàn)樗麜?huì)造成阻塞等待, 非必要還是不要進(jìn)行加鎖.
以下代碼在加鎖的基礎(chǔ)上, 做出了進(jìn)一步改動(dòng):
- 使用雙重 if 判定, 降低鎖競(jìng)爭(zhēng)的頻率.
- 給 instance 加上了 volatile.
class SingletonLazy {
private static volatile SingletonLazy instance = null;
public static SingletonLazy getInstance() {
if (instance == null) {
synchronized (SingletonLazy.class) {
if (instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
private SingletonLazy() {};
}
理解雙重 if 判定 / volatile:
加鎖 / 解鎖是一件開(kāi)銷(xiāo)比較高的事情. 而懶漢模式的線程不安全只是發(fā)生在首次創(chuàng)建實(shí)例的時(shí)候. 因此后續(xù)使用的時(shí)候, 不必再進(jìn)行加鎖了.
- 外層的 if 就是判定下看當(dāng)前是否已經(jīng)把 instance 實(shí)例創(chuàng)建出來(lái)了.
- 同時(shí)為了避免 “內(nèi)存可見(jiàn)性” 導(dǎo)致讀取的 instance 出現(xiàn)偏差, 于是補(bǔ)充上 volatile .
- 當(dāng)多線程首次調(diào)用 getInstance, 大家可能都發(fā)現(xiàn) instance 為 null, 于是又繼續(xù)往下執(zhí)行來(lái)競(jìng)爭(zhēng)鎖, 其中競(jìng)爭(zhēng)成功的線程, 再完成創(chuàng)建實(shí)例的操作.
- 當(dāng)這個(gè)實(shí)例創(chuàng)建完了之后, 其他競(jìng)爭(zhēng)到鎖的線程就被里層 if 擋住了. 也就不會(huì)繼續(xù)創(chuàng)建其他實(shí)例.
- 有三個(gè)線程, 開(kāi)始執(zhí)行 getInstance , 通過(guò)外層的 if (instance == null) 知道了實(shí)例還沒(méi)有創(chuàng)建的消息. 于是開(kāi)始競(jìng)爭(zhēng)同一把鎖.
總結(jié)
? 本文講解了線程安全下的單例模式, 由于餓漢模式只是讀操作, 天生就是安全的, 而懶漢模式不是安全的, 因?yàn)橛袑?xiě)操作, 我們通過(guò)加鎖, 并利用雙重 if 來(lái)減少不必要的加鎖操作, 再使用 volatile 禁止指令重排序, 使其變得安全.
? 想了解更多的多線程知識(shí), 可以收藏一下本人的多線程學(xué)習(xí)專(zhuān)欄, 里面會(huì)持續(xù)更新本人的學(xué)習(xí)記錄, 跟隨我一起不斷學(xué)習(xí).
? 感謝你們的耐心閱讀, 博主本人也是一名學(xué)生, 也還有需要很多學(xué)習(xí)的東西. 寫(xiě)這篇文章是以本人所學(xué)內(nèi)容為基礎(chǔ), 日后也會(huì)不斷更新自己的學(xué)習(xí)記錄, 我們一起努力進(jìn)步, 變得優(yōu)秀, 小小菜鳥(niǎo), 也能有大大夢(mèng)想, 關(guān)注我, 一起學(xué)習(xí).文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-630602.html
再次感謝你們的閱讀, 你們的鼓勵(lì)是我創(chuàng)作的最大動(dòng)力!!!!!
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-630602.html
到了這里,關(guān)于【多線程初階】多線程案例之單例模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!