java23種設(shè)計模式—— 二挚瘟、單例模式

源碼在我的githubgitee中獲取

介紹

單例模式(Singleton Pattern)是 Java 中最簡單的設(shè)計模式之一回怜。這種類型的設(shè)計模式屬于創(chuàng)建型模式仓洼,它提供了一種創(chuàng)建對象的最佳方式公壤。

這種模式涉及到一個單一的類,該類負(fù)責(zé)創(chuàng)建自己的對象商模,同時確保只有單個對象被創(chuàng)建奠旺。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問施流,不需要實例化該類的對象响疚。

注意:

  • 1、單例類只能有一個實例瞪醋。

  • 2忿晕、單例類必須自己創(chuàng)建自己的唯一實例。

  • 3趟章、單例類必須給所有其他對象提供這一實例杏糙。

實現(xiàn)方式

餓漢式單例(靜態(tài)常量慎王,線程安全)

顧名思義,餓漢式單例它很“餓”宏侍,所以一開始就創(chuàng)建了唯一但單例實例赖淤,但如果你沒有使用過這個實例,就會造成內(nèi)存的浪費


/**

* 餓漢式單例

* 優(yōu)點:簡單谅河,在類裝載時就完成了實例化咱旱,避免了線程同步問題,線程安全

* 缺點:由于這個類已經(jīng)完成了實例化绷耍,如果從始至終都沒有用過這個實例吐限,就會造成內(nèi)存的浪費

  */

public class SingletonTest01 {

    public static void main(String[] args) {

        Signleton instance1= Signleton.getInstance();

        Signleton instance2 = Signleton.getInstance();

        System.out.println(instance1==instance2);

        System.out.println(instance1.hashCode());

        System.out.println(instance2.hashCode());

    }

}

class Signleton{

    //1、構(gòu)造器私有化,外部無法通過new新建

    private Signleton(){ }

    //2褂始、內(nèi)部創(chuàng)建對象實例

    private final static Signleton instance = new Signleton();

    //3诸典、提供一個公有的靜態(tài)方法,返回實例對象

    public final static Signleton getInstance(){

        return instance;

    }

}

輸出結(jié)果


true

1163157884

1163157884

可以看到輸出的是同一個實例

餓漢式單例(靜態(tài)代碼塊崎苗,線程安全)

和之前的方式類似狐粱,只不過將類實例化的過程放在了靜態(tài)代碼塊中,也就是類裝載的時候胆数,

就執(zhí)行靜態(tài)代碼塊中的代碼肌蜻,優(yōu)缺點和之前一樣


/**

* 和之前的方式類似,只不過將類實例化的過程放在了靜態(tài)代碼塊中必尼,也就是類裝載的時候蒋搜,

* 就執(zhí)行靜態(tài)代碼塊中的代碼,優(yōu)缺點和之前一樣

*/

public class SingletonTest02 extends Thread{

    public static void main(String[] args) {

        Signleton instance1= Signleton.getInstance();

        Signleton instance2 = Signleton.getInstance();

        System.out.println(instance1==instance2);

        System.out.println(instance1.hashCode());

        System.out.println(instance2.hashCode());

    }

}

class Signleton{

    //1判莉、構(gòu)造器私有化,外部無法通過new新建

    private Signleton(){}

    //2豆挽、內(nèi)部創(chuàng)建對象實例

    private static Signleton instance;

    static {//靜態(tài)代碼塊種,創(chuàng)建單例對象

        instance = new Signleton();

    }

    //3券盅、提供一個公有的靜態(tài)方法祷杈,返回實例對象

    public final static Signleton getInstance(){

        return instance;

    }

}

輸出


true

1163157884

1163157884

懶漢式(線程不安全)

同樣,顧名思義渗饮,懶漢式單例它很懶。只有在你用到它時宿刮,它才會創(chuàng)建一個實例互站。


/**

* 餓漢式-線程不安全

* 優(yōu)點:起到了懶加載的效果,但是只能在單線程下使用

* 如果在多線程下僵缺,如果一個線程進(jìn)入了if判斷語句塊胡桃,

* 還沒來得及向下執(zhí)行,另一個線程也進(jìn)入這個判斷語句磕潮,就會產(chǎn)生多個實例(違背單例模式)翠胰,

* 實際開發(fā)中容贝,不要使用這種方式

*/

public class SingletonTest03 {

    public static void main(String[] args) {

        for (int i = 0; i <10 ; i++) {

            new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();

        }

    }

}

class Signleton{

    private static Signleton instance;

    private Signleton(){}

    //提供一個靜態(tài)的公有方法,當(dāng)調(diào)用方法時之景,才去創(chuàng)建instance

    public static Signleton getInstance(){

        if(instance == null){//如果為空再去創(chuàng)建對象

            instance = new Signleton();

        }

        return instance;

    }

}

輸出


546405844

135417039

135417039

802181073

135417039

135417039

135417039

802181073

135417039

135417039

這里我選了個比較極端的情況斤富,如果你的電腦配置比較好,可能運行幾次結(jié)果都是符合單例模式的锻狗。

懶漢式(同步方法满力,線程安全)

上面方法之所以會存在線程不安全的情況,是因為多線程情況下轻纪,可能會有多條線程同時判斷單例是否創(chuàng)建油额。那么要解決這個問題 ,只需要同步getInstance()方法


/**

* 解決了線程不安全的問題

* 但是大大降低了效率 每個線程想獲得實例的時候刻帚,執(zhí)行g(shù)etInstance()方法都要進(jìn)行同步

*/

public class SingletonTest04 {

    public static void main(String[] args) {

        for (int i = 0; i <10 ; i++) {

            new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();

        }

    }

}

class Signleton{

    private static Signleton instance;

    private Signleton(){}

    //提供一個靜態(tài)的公有方法潦嘶,當(dāng)調(diào)用方法時,才去創(chuàng)建instance

    public static synchronized Signleton getInstance(){

        if(instance == null){//如果為空再去創(chuàng)建對象

            instance = new Signleton();

        }

        return instance;

    }

}

結(jié)果


802181073

802181073

802181073

802181073

802181073

802181073

802181073

802181073

802181073

802181073

但是崇众,synchronized是一個很重量的同步鎖掂僵,而我們每次執(zhí)行g(shù)etInstance()時都會進(jìn)行同步,極其影響效率

懶漢式(雙重檢查校摩,線程安全)

雙檢鎖看峻,又叫雙重校驗鎖,綜合了懶漢式和餓漢式兩者的優(yōu)缺點整合而成衙吩』ゼ耍看上面代碼實現(xiàn)中,特點是在synchronized關(guān)鍵字內(nèi)外都加了一層 if 條件判斷坤塞,這樣既保證了線程安全冯勉,又比直接上鎖提高了執(zhí)行效率,還節(jié)省了內(nèi)存空間


/**

* 懶漢模式-雙重檢查

* 進(jìn)行了兩次if判斷檢查摹芙,這樣就保證線程安全了

* 通過判斷是否為空灼狰,來確定是否 需要再次實例化

*/

public class SingletonTest05 {

    public static void main(String[] args) {

        for (int i = 0; i <10 ; i++) {

            new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();

        }

    }

}

class Signleton{

    private static volatile Signleton instance;//volatile保證可見性

    private Signleton(){}

    //提供一個靜態(tài)的公有方法,加入雙重檢查代碼浮禾,解決線程安全問題交胚,同時解決懶加載問題

    public static Signleton getInstance() {

        if (instance == null) {

            synchronized (Signleton.class) {

                if (instance == null) {

                    instance = new Signleton();

                }

            }

        }

        return instance;

    }

}

運行結(jié)果


79372097

79372097

79372097

79372097

79372097

79372097

79372097

79372097

79372097

79372097

推薦使用

靜態(tài)內(nèi)部類(線程安全)


/**

* 靜態(tài)內(nèi)部類實現(xiàn)單例模式

* 該方法采用了類裝載機(jī)制來保證初始化實例時只有一個線程

* 靜態(tài)內(nèi)部類在Signleton類被裝載時并不會立即實例化,而是需要實例化時盈电,才會裝載SignletonInstance類

* 類的靜態(tài)屬性只會在第一次加載類的時候初始化

* 避免了線程不安全蝴簇,利用靜態(tài)內(nèi)部類實現(xiàn)懶加載,效率高

*/

public class SingletonTest07 {

    public static void main(String[] args) {

        for (int i = 0; i <10 ; i++) {

            new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();

        }

    }

}

class Signleton{

    //構(gòu)造器私有

    private Signleton(){}

    //靜態(tài)內(nèi)部類匆帚,該類中有一個靜態(tài)屬性Signleton

    private static class SignletonInstance{

        private static final Signleton instance = new Signleton();

    }

    //提供一個靜態(tài)的公有方法熬词,直接返回SignletonInstance.instance

    public static Signleton getInstance() {

        return SignletonInstance.instance;

    }

}

結(jié)果


79372097

79372097

79372097

79372097

79372097

79372097

79372097

79372097

79372097

79372097

這種方式較為簡單,推薦使用

枚舉(線程安全)


/**

* @author codermy

* @createTime 2020/5/14

* 枚舉方法實現(xiàn)單例模式

* 借助jdk1.5中添加的枚舉類來實現(xiàn)單例模式,

* 不僅能避免多線程同步問題互拾,而且還能防止反序列化重新創(chuàng)建新對象

*/

public class SingletonTest08 {

    public static void main(String[] args) {

        Singleton singleton = Singleton.INSTANCE;

        singleton.Ok();

        for (int i = 0; i <10 ; i++) {

            new Thread(() -> System.out.println(Singleton.INSTANCE.hashCode()) ).start();

        }

    }

}

enum Singleton{

    INSTANCE;//屬性

    public void Ok(){

        System.out.println("ok");

    }

}

結(jié)果


ok

858497792

858497792

858497792

858497792

858497792

858497792

858497792

858497792

858497792

858497792

可以看出歪今,枚舉實現(xiàn)單例模式,最為簡潔颜矿,較為推薦寄猩。但是正是因為它簡潔,導(dǎo)致可讀性較差

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末或衡,一起剝皮案震驚了整個濱河市焦影,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌封断,老刑警劉巖斯辰,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異坡疼,居然都是意外死亡彬呻,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門柄瑰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闸氮,“玉大人,你說我怎么就攤上這事教沾∑芽纾” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵授翻,是天一觀的道長或悲。 經(jīng)常有香客問我,道長堪唐,這世上最難降的妖魔是什么巡语? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮淮菠,結(jié)果婚禮上男公,老公的妹妹穿的比我還像新娘。我一直安慰自己合陵,他們只是感情好枢赔,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拥知,像睡著了一般糠爬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上举庶,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機(jī)與錄音揩抡,去河邊找鬼户侥。 笑死镀琉,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蕊唐。 我是一名探鬼主播屋摔,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼替梨!你這毒婦竟也來了钓试?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤副瀑,失蹤者是張志新(化名)和其女友劉穎弓熏,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糠睡,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡挽鞠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了狈孔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片信认。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖均抽,靈堂內(nèi)的尸體忽然破棺而出嫁赏,到底是詐尸還是另有隱情,我是刑警寧澤油挥,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布潦蝇,位于F島的核電站,受9級特大地震影響喘漏,放射性物質(zhì)發(fā)生泄漏护蝶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一翩迈、第九天 我趴在偏房一處隱蔽的房頂上張望持灰。 院中可真熱鬧,春花似錦负饲、人聲如沸堤魁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妥泉。三九已至,卻和暖如春洞坑,著一層夾襖步出監(jiān)牢的瞬間盲链,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留刽沾,地道東北人本慕。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像侧漓,于是被迫代替她去往敵國和親锅尘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353