夏津網(wǎng)站建設(shè)電話百度提升排名
目錄
餓漢模式
懶漢模式-單線程版
懶漢模式-多線程版
懶漢模式-多線程版(改進(jìn))
單例是一種設(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è)類在程序中只存在唯一一份實(shí)例, 而不會(huì)創(chuàng)建出多個(gè)實(shí)例.
這一點(diǎn)在很多場(chǎng)景上都需要 . 比如 JDBC 中的 DataSource 實(shí)例就只需要一個(gè) .
單例模式具體的實(shí)現(xiàn)方式 , 分成 " 餓漢 " 和 " 懶漢 " 兩種 .
首先要分析清楚,在JAVA中哪些對(duì)象是全局唯一的:
.class對(duì)象? ?比如String.class;
用static修飾的變量。
餓漢模式
類加載的同時(shí) , 創(chuàng)建實(shí)例 .
class Singleton {private static Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}
? ? 既然是單例,那么通過(guò)new方式去獲取對(duì)象是有歧義的,所以要將構(gòu)造方法私有化,不能讓外部去new這個(gè)對(duì)象。
餓漢模式書寫簡(jiǎn)單,不容易出錯(cuò)。
懶漢模式-單線程版
類加載的時(shí)候不創(chuàng)建實(shí)例 . 第一次使用的時(shí)候才創(chuàng)建實(shí)例。
class Singleton {private static Singleton instance = null;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
但是放在多線程中,就會(huì)創(chuàng)建多個(gè)對(duì)象,出現(xiàn)線程不安全現(xiàn)象。
懶漢模式-多線程版
上面的懶漢模式的實(shí)現(xiàn)是線程不安全的。
線程安全問(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 了 )
加上 synchronized 可以改善這里的線程安全問(wèn)題 .
class Singleton {private static Singleton instance = null;private Singleton() {}public synchronized static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
懶漢模式-多線程版(改進(jìn))
以下代碼在加鎖的基礎(chǔ)上 , 做出了進(jìn)一步改動(dòng):
- 使用雙重 if 判定, 降低鎖競(jìng)爭(zhēng)的頻率.
- 給 instance 加上了 volatile.
class Singleton {private static volatile Singleton instance = null;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}
理解雙重 if 判定 / volatile:加鎖 / 解鎖是一件開(kāi)銷比較高的事情 . 而懶漢模式的線程不安全只是發(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í)例 .
常見(jiàn)面試題:
描述一下單例模式:
?