柚子快報邀請碼778899分享:開發(fā)語言 【Java】單例模式
柚子快報邀請碼778899分享:開發(fā)語言 【Java】單例模式
單例模式是面試中常考的設(shè)計模式之一 在面試中,面試官常常會要求寫出兩種類型的單例模式并解釋原理 本文中,將從0到1的介紹單例模式究竟是什么
文章目錄
?一、什么是設(shè)計模式??二、單例模式是什么??三、單例模式的類型**1.餓漢式**2.懶漢式3.優(yōu)化懶漢式4.指令重排5.完整代碼
?一、什么是設(shè)計模式?
設(shè)計模式(Design pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類編目的、代碼設(shè)計經(jīng)驗的總結(jié)。使用設(shè)計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設(shè)計模式于己于他人于系統(tǒng)都是多贏的,設(shè)計模式使代碼編制真正工程化,設(shè)計模式是軟件工程的基石,如同大廈的一塊塊磚石一樣。項目中合理的運用設(shè)計模式可以完美的解決很多問題,每種模式在現(xiàn)在中都有相應(yīng)的原理來與之對應(yīng),每一個模式描述了一個在我們周圍不斷重復(fù)發(fā)生的問題,以及該問題的核心解決方案,這也是它能被廣泛應(yīng)用的原因。簡單說:
模式:在某些場景下,針對某類問題的某種通用的解決方案。
場景:項目所在的環(huán)境
問題:約束條件,項目目標等
解決方案:通用、可復(fù)用的設(shè)計,解決約束達到目標。
用生活中的事務(wù)來介紹:
設(shè)計模式好?象棋中的 “棋譜”. 紅?當頭炮, ???來跳. 針對紅?的?些?法, ??應(yīng)招的時候有?些固定的套路. 按照套路來?局勢就不會吃虧.
?二、單例模式是什么?
單例模式是指在內(nèi)存中只會創(chuàng)建且僅創(chuàng)建一次對象的設(shè)計模式。在程序中多次使用同一個對象且作用相同時,為了防止頻繁地創(chuàng)建對象使得內(nèi)存飆升,單例模式可以讓程序僅在內(nèi)存中創(chuàng)建一個對象,讓所有需要調(diào)用的地方都共享這一單例對象。
簡單概括:
單例模式能保證某個類在程序中只存在唯??份實例,而不會創(chuàng)建出多個實例.
?三、單例模式的類型
單例模式具體的實現(xiàn)?式有很多. 最常?的是 “餓漢” 和 “懶漢” 兩種.
餓漢式:在類加載過程中就創(chuàng)建了實例。懶漢式:在真正需要使用時,才會創(chuàng)建實例。
1.餓漢式
class Singleton{
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
private Singleton(){}
}
類在加載時,就會創(chuàng)建一個實例。在調(diào)用時,之間返回這一實例就好。
可以簡單的認為,在程序啟動時就創(chuàng)建了實例。
2.懶漢式
class Singletonlazy{
public static Singletonlazy instance = null;
public Singletonlazy getInstance(){
if ( instance == null){
instance = new Singletonlazy();
}
return instance;
}
private Singletonlazy(){}
}
這是一段存在些許問題的代碼,不過可以直觀的感受到兩者之間的區(qū)別。
在接下來,會對如上懶漢式代碼進行優(yōu)化。
3.優(yōu)化懶漢式
我們先將懶漢式代碼放置如下
class Singletonlazy{
public static Singletonlazy instance = null;
public Singletonlazy getInstance(){
if ( instance == null){
instance = new Singletonlazy();
}
return instance;
}
private Singletonlazy(){}
}
在如上懶漢式的代碼中,如果在多線程情況下,就會出現(xiàn)一些問題
在多線程中,線程是搶占式執(zhí)行的。 那么就會給程序帶來一些問題
由于線程的搶占式執(zhí)行,雖說不會造成空間的浪費 但是時間的消耗確實客觀存在的。
那么解決這個問題,就進行加鎖操作。
public Singletonlazy getInstance(){
synchronized (lock){
if ( instance == null){
instance = new Singletonlazy();
}
}
return instance;
}
這樣加鎖,就是將 if 和 new 打包成一個原子操作
但是這樣也會出現(xiàn)問題
那么如何解決這個問題呢? 我們在鎖的外層,在添加一個判斷條件
public Singletonlazy getInstance(){
if (instance == null) {
synchronized (lock){
if ( instance == null){
instance = new Singletonlazy();
}
}
}
return instance;
}
注意: 這里的兩個if條件雖然內(nèi)容一樣,但是意義卻完全不同
第一個if是判斷是否要進行加鎖操作第二個if是判斷是否要實例創(chuàng)建對象
如上代碼已經(jīng)解決了多線程情況下的線程安全問題。 也解決了執(zhí)行效率的問題。
但是還存在一個問題 指令重排
4.指令重排
概念:
為了使處理器內(nèi)部的運算單元能盡量被充分利用,處理器可能會對輸入的代碼進行亂序執(zhí)行優(yōu)化,處理器會在計算之后將亂序執(zhí)行的結(jié)果重組,并確保這一結(jié)果和順序執(zhí)行結(jié)果是一致的,但是這個過程并不保證各個語句計算的先后順序和輸入代碼中的順序一致。這就是指令重排序。
通俗的說,就是在不改變代碼邏輯的條件下,通過更改指令的執(zhí)行順序,來達到優(yōu)化代碼的效果。
舉例:
在這一行代碼中,一個創(chuàng)建對象實例的過程可以在指令的角度分為三步
申請內(nèi)容空間調(diào)用構(gòu)造方法(對內(nèi)存空間進行初始化)把此時內(nèi)存空間的地址,賦值給instance引用
在指令重排的優(yōu)化下 可能有 1 --》3 --》 2 1 --》2 --》 3 這樣兩種情況
1是一定在第一步的,因為是要在保證代碼邏輯的前提下,才能進行指令重排。
那么如何解決呢? 引入volatile
public static volatile Singletonlazy instance = null;
使用volatile關(guān)鍵字修飾的變量,可以保證其指令執(zhí)行的順序與程序指明的順序一致,不會發(fā)生順序變換
5.完整代碼
class Singletonlazy{
public static volatile Singletonlazy instance = null;
public static Object lock = new Object();
public Singletonlazy getInstance(){
if (instance == null) {
synchronized (lock){
if ( instance == null){
instance = new Singletonlazy();
}
}
}
return instance;
}
private Singletonlazy(){}
}
以上就是本文所有內(nèi)容,如果對你有幫助的話,點贊收藏支持一下吧!???
柚子快報邀請碼778899分享:開發(fā)語言 【Java】單例模式
參考閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。