二維碼
        企資網(wǎng)

        掃一掃關(guān)注

        當(dāng)前位置: 首頁 » 企業(yè)資訊 » 資訊 » 正文

        單例模式,關(guān)鍵字級別詳解

        放大字體  縮小字體 發(fā)布日期:2021-09-01 05:50:36    作者:企資小編    瀏覽次數(shù):127
        導(dǎo)讀

        大家好,我是課代表。歡迎關(guān)注我的公眾號_Java課代表,原創(chuàng)實(shí)戰(zhàn)干貨首發(fā)地兒,不要錯過呦!來,我們開始今天的分享!0.前言如果你去問一個寫過幾年代碼的程序員用過哪些設(shè)計(jì)模式,我打賭,90_以上的回答里面會帶【單

        大家好,我是課代表。歡迎關(guān)注我的公眾號_Java課代表,原創(chuàng)實(shí)戰(zhàn)干貨首發(fā)地兒,不要錯過呦!

        來,我們開始今天的分享!

        0.前言

        如果你去問一個寫過幾年代碼的程序員用過哪些設(shè)計(jì)模式,我打賭,90_以上的回答里面會帶【單例模式】。甚至有的面試官會直接問_說一下你用過哪些設(shè)計(jì)模式,單例就不用說了。你看,連面試官都聽煩了,火爆程度可見一斑。

        不過,看似簡單的單例模式,里面蘊(yùn)含了很多Java基礎(chǔ),日常開發(fā)過程中課代表見過很多不規(guī)范的,甚至是有問題的單例實(shí)現(xiàn)。所以整理此文,總結(jié)一下單例模式的最佳實(shí)踐。

        1、懶加載(懶漢)

        所謂懶加載,就是直到第一次被調(diào)用時才加載。其實(shí)現(xiàn)需要考慮并發(fā)問題和指令重排,代碼如下_

        public class Singleton {    private volatile static Singleton instance; //①    private Singleton() { //②    }    public static Singleton getInstance() {        if (instance __ null) {//③            synchronized (Singleton.class) {                if (instance __ null) {//④                    instance _ new Singleton();//⑤                }            }        }        return instance;    }}

        這段代碼精簡至極,沒有一個字符是多余的,下面逐行解讀一下_

        首先,注意到①處的volatile關(guān)鍵字,她具備兩項(xiàng)特性_

        一是保證此變量對于所有線程的可見性。即當(dāng)一條線程修改了這個變量的值,新值對于其他線程來說是可以立即得知的。

        二是禁止指令重排序優(yōu)化。

        這里解釋一下指令重排序優(yōu)化_

        代碼 ⑤ 處的instance _ new Singleton();并不是原子的,大體可分為如下 3 步_

          分配內(nèi)存調(diào)用構(gòu)造函數(shù)初始化成實(shí)例
        1. instance指向分配的內(nèi)存空間

        JVM 允許在保證結(jié)果正確的前提下進(jìn)行指令重排序優(yōu)化。即如上 3 步可能的順序?yàn)?->2->3 或 1->3->2 。如果順序是 1->3->2 ,當(dāng) 3 執(zhí)行完,2 還未執(zhí)行時,另一個線程執(zhí)行到代碼 ③ 處,發(fā)現(xiàn)instance不為null,直接返回還未初始化好的instance并使用,就會報錯。

        所以使用volatile,就是為了保證線程間的可見性和防止指令重排。

        其次,代碼②處將構(gòu)造函數(shù)聲明為private目的在于阻止使用new Singleton()這樣的代碼生成新實(shí)例。

        最后,當(dāng)客戶端調(diào)用Singleton.getInstance()時,先檢查是否已經(jīng)實(shí)例化(代碼③),未實(shí)例化時同步代碼塊,然后再次檢查是否已實(shí)例化(代碼④),然后才執(zhí)行代碼⑤。兩次檢查的意義在于,防止synchronized同步過程中其他線程進(jìn)行了實(shí)例化。

        這就是著名的雙重檢查鎖(Double check lock)實(shí)現(xiàn)單例,也即懶加載。

        TIPS:

        網(wǎng)上也有直接對getInstance()方法加鎖的版本,這樣大范圍的方法級別加鎖會導(dǎo)致并發(fā)變低,實(shí)際上第一次調(diào)用生成實(shí)例之后,后續(xù)獲取實(shí)例根本不需要并發(fā)控制了。而本例的雙重檢查鎖版本可以避免此并發(fā)問題。

        2、預(yù)加載(餓漢)

        與懶加載相對應(yīng),預(yù)加載是在類加載時就已經(jīng)初始化好了,所以是天然線程安全的,代碼如下_

        public class Singleton {    private static final Singleton instance _ new Singleton();// ①        private Singleton(){}        public static Singleton getInstance(){        return instance;    }}

        注意到 ① 處的類變量使用了final

        這里用final更多的意義在于提供語法約束。畢竟你是單例,就只有這一個實(shí)例,不可能再指向另一個。instance有了final的約束,后面再有人不小心編寫了修改其指向的代碼就會報語法錯誤。

        這就好比@Override注解,你能保證寫對方法名和參數(shù),那不寫注解也沒問題,但是有了注解的約束,編譯器就會幫你檢查,還能防止別人亂改。

        3、靜態(tài)內(nèi)部類

        此方法和預(yù)加載原理相同,都是利用JVM類加載的特性實(shí)現(xiàn)天然的線程安全,不同之處在于,靜態(tài)內(nèi)部類做到了延遲加載。

        public class Singleton {        private static class SingletonHolder {        private static Singleton instance _ new Singleton();    }        private Singleton(){}    public static Singleton getInstance() {        return SingletonHolder.instance;    }}

        SingletonHolder 是靜態(tài)內(nèi)部類,當(dāng)外部類Singleton被加載的時候并不會創(chuàng)建任何實(shí)例,只有當(dāng)Singleton.getInstance()被調(diào)用的時候,才會創(chuàng)建Singleton實(shí)例,這一切由 JVM 天然完成,所以既保證了線程安全,又實(shí)現(xiàn)了延遲加載。

        4、枚舉

        沒錯,枚舉可以實(shí)現(xiàn)單例,而且這種方式是《Effective Java中文版》第二版 中的推薦實(shí)現(xiàn)方式。代碼極其簡單_

        public enum Singleton {        INSTANCE;    public void doSomeThing(){        System.out.println("done");    }}

        使用時直接Singleton.INSTANCE.doSomeThing();即可。

        這里主要利用了枚舉的如下兩個特性_

        枚舉的構(gòu)造器總是私有的,所以不必像前幾種方式一樣顯式定義私有構(gòu)造方法
      1. 枚舉類中的每個值,都是實(shí)例(只有INSTANCE這一個實(shí)例)

        除此之外,枚舉還附帶了一些額外好處_無償?shù)靥峁┝诵蛄谢瘷C(jī)制,還可以防止通過多次反序列化生成多個實(shí)例。

        鑒于此,單例的最佳實(shí)踐就是用枚舉來實(shí)現(xiàn)。

        5、總結(jié)

        事實(shí)上,單例的寫法并不止于本文所提的這 4 種,你可能還會看到很多其他變種,她們或多或少都存在一些缺陷,比如,懶加載方式將synchronized作用于整個方法上也能實(shí)現(xiàn),但頻繁加鎖,釋放鎖會產(chǎn)生性能瓶頸,而完全去掉鎖又會帶來并發(fā)問題。

        所以,只要吃透了文中列出的這 4 種單例方式,就能做到舉一反三,見到別人寫的單例也能一眼看出對錯。

        文中所列的 4 種單例模式,除了枚舉之外,全都用到了static關(guān)鍵字,《Java 虛擬機(jī)規(guī)范》 規(guī)定,有幾種情況必須立即對類進(jìn)行“初始化”,其中涉及static的場景如下_

        讀取或設(shè)置一個類型的靜態(tài)字段(被 final 修飾、已在編譯期把結(jié)果放入常量池的靜態(tài)字段除外)的時候。

        調(diào)用一個類型的靜態(tài)方法的時候。

        懶加載,預(yù)加載和靜態(tài)內(nèi)部類正是利用了這兩點(diǎn)特性。

        static關(guān)鍵字遺忘的同學(xué)可以參看我的另一篇文章_《一題搞定static關(guān)鍵字》

        最后,再次強(qiáng)調(diào)一下,如果大家開發(fā)中需要手寫單例,建議聽從 Joshua Bloch在《Effective Java中文版》第二版 中的建議_

        單元素的枚舉類型已經(jīng)成為實(shí)現(xiàn) Singleton 的最佳方法

        參考資料_

        1、《Effective Java中文版》 Joshua Bloch 第二版 P15

        2、《深入理解 Java 虛擬機(jī)》 周志明 第3版,P444-P448,P264

        3、深入淺出單實(shí)例SINGLETON設(shè)計(jì)模式

        相關(guān)原創(chuàng)推薦

        一題搞定static關(guān)鍵字

      2.  
        (文/企資小編)
        免責(zé)聲明
        本文僅代表作發(fā)布者:企資小編個人觀點(diǎn),本站未對其內(nèi)容進(jìn)行核實(shí),請讀者僅做參考,如若文中涉及有違公德、觸犯法律的內(nèi)容,一經(jīng)發(fā)現(xiàn),立即刪除,需自行承擔(dān)相應(yīng)責(zé)任。涉及到版權(quán)或其他問題,請及時聯(lián)系我們刪除處理郵件:weilaitui@qq.com。
         

        Copyright ? 2016 - 2025 - 企資網(wǎng) 48903.COM All Rights Reserved 粵公網(wǎng)安備 44030702000589號

        粵ICP備16078936號

        微信

        關(guān)注
        微信

        微信二維碼

        WAP二維碼

        客服

        聯(lián)系
        客服

        聯(lián)系客服:

        在線QQ: 303377504

        客服電話: 020-82301567

        E_mail郵箱: weilaitui@qq.com

        微信公眾號: weishitui

        客服001 客服002 客服003

        工作時間:

        周一至周五: 09:00 - 18:00

        反饋

        用戶
        反饋

        主站蜘蛛池模板: 亚洲一区二区三区不卡在线播放| 欧洲精品无码一区二区三区在线播放| 在线视频精品一区| 国产一区二区三区小向美奈子 | 中文字幕一区二区日产乱码| 欧美日韩精品一区二区在线观看| 亚洲国产精品自在线一区二区| 狠狠做深爱婷婷综合一区 | 国产精品一区二区四区| 精品深夜AV无码一区二区| 精品一区二区三区高清免费观看 | 日韩一区二区三区不卡视频| 亚洲午夜一区二区电影院| 日韩欧国产精品一区综合无码| 亚洲AⅤ视频一区二区三区| 欧洲精品码一区二区三区免费看 | 国产精品视频一区二区噜噜| 精品国产AⅤ一区二区三区4区 | 国产美女在线一区二区三区| 亚洲一区二区在线免费观看| 亚洲国产精品一区第二页| 国产精品日本一区二区在线播放 | 人妻无码一区二区三区AV| 精品亚洲一区二区| 亚洲av午夜福利精品一区| 无码人妻AⅤ一区二区三区| 亚洲AV乱码一区二区三区林ゆな| 精品一区二区在线观看| 久久国产精品亚洲一区二区| 久久久精品人妻一区二区三区 | 香蕉久久av一区二区三区| 亚洲国产成人一区二区三区| 亚洲一区无码中文字幕乱码| 91一区二区三区四区五区| 欧美日韩一区二区成人午夜电影| 国产剧情一区二区| 波多野结衣高清一区二区三区| 亚洲AV综合色一区二区三区| 日韩国产免费一区二区三区| 在线视频精品一区| 香蕉视频一区二区三区|