定義:
            單例模式(singleton),保證一個(gè)類僅有一個(gè)實(shí)例,并且提供一個(gè)訪問它的全局訪問點(diǎn)。   這句話很好理解,今天我們的重點(diǎn)也不在于如何解讀單例模式。
            在面試的過程中,往往會(huì)遇到考察手寫單例模式的場(chǎng)景,今天讓我們關(guān)注一下,寫單例模式的幾種方法。
          餓漢式:


          /** * 餓漢式. * * @author jialin.li * @date 2019-12-30 22:13 */ public class
          Singleton {private Singleton() { } private static Singleton singleton = new
          Singleton();public Singleton getInstance(){ return singleton; } }
          *
          * 餓漢式的特點(diǎn)是類初始化的時(shí)候,創(chuàng)建了該對(duì)象。
          * 由于類只會(huì)初始化一次,所以保證了對(duì)象只會(huì)被創(chuàng)建一次。
          * 同時(shí)將構(gòu)造方法私有化,保證了沒有辦法從外部創(chuàng)建對(duì)象。   這種方法的問題是,如果該實(shí)例從始至終都沒有被使用過,就會(huì)造成內(nèi)存的浪費(fèi)。
          懶漢式:
          /** * 懶漢式. * * @author jialin.li * @date 2019-12-30 22:13 */ public class
          Singleton {private Singleton() { } private volatile Singleton singleton = null;
          public Singleton getInstance(){ // 提高性能,降低線程進(jìn)入臨界區(qū)的可能 if(singleton == null){
          synchronized (Singleton.class){ if(singleton == null){ singleton = new
          Singleton(); } } }return singleton; } }   這種寫法又被成為雙檢鎖模式,是一種實(shí)現(xiàn)單例模式的經(jīng)典寫法。
            代碼中有兩處判空:
          *
          * 第一處判空,是為了提高性能,降低線程進(jìn)入臨界區(qū)的可能性。
          *
          第二處判空是為了線程同步,假如沒有第二處判空,則可能兩個(gè)線程都通過了if(singleton==null)條件,這樣即使是臨界區(qū)內(nèi)只有一個(gè)線程在執(zhí)行,臨界區(qū)內(nèi)的代碼也會(huì)被執(zhí)行兩遍,這樣就會(huì)產(chǎn)生兩個(gè)對(duì)象,不符合單例模式。
            成員變量使用了volatile進(jìn)行修飾,一方面是保證了對(duì)象在多線程環(huán)境下的可見性,另一方面是為了防止new
          Singleton()進(jìn)行指令重排序而導(dǎo)致的并發(fā)問題。   volatile關(guān)鍵字的作用兩個(gè):
          *
          * 保證變量在線程之間的可見性(直接從主存中讀寫數(shù)據(jù),不經(jīng)過工作內(nèi)存)
          * 阻止編譯時(shí)和運(yùn)行時(shí)的指令重排,編譯時(shí)JVM編譯器遵循內(nèi)存屏障約束,運(yùn)行時(shí)依賴CPU屏障來(lái)阻止指令重排。
            指令重排是指JVM在編譯Java代碼的時(shí)候,或者CPU在執(zhí)行JVM字節(jié)碼的時(shí)候,對(duì)現(xiàn)有的指令順序進(jìn)行重新排序。
            指令重排的目的是為了在不改變程序執(zhí)行結(jié)果的前提下,優(yōu)化程序的運(yùn)行效率。需要注意的是,這里所說的不改變執(zhí)行結(jié)果,指的是不改變單線程下的程序執(zhí)行結(jié)果。
          這里不太好懂,舉一個(gè)例子,正常的new Singleton()創(chuàng)建步驟是:
          * 開辟一塊內(nèi)存空間
          * 創(chuàng)建對(duì)象
          * 將對(duì)象的地址存入引用變量   經(jīng)過指令重排后,可能變成了:
          * 開辟一塊內(nèi)存空間
          * 將對(duì)象的地址存入引用變量
          * 創(chuàng)建對(duì)象
            假設(shè)發(fā)生了指令重排,線程A、B都執(zhí)行這段代碼,線程A執(zhí)行到了new
          Singleton()的步驟2,此時(shí)還沒有創(chuàng)建對(duì)象,這個(gè)時(shí)候發(fā)生了線程的切換。線程B開始執(zhí)行,這個(gè)時(shí)候線程B還可以通過if(singleton ==
          null)的判斷,因?yàn)榫€程A中的singleton只是指向了一個(gè)空的內(nèi)存地址,這個(gè)時(shí)候線程B創(chuàng)建出了一個(gè)Singleton對(duì)象,當(dāng)線程切換成A時(shí),線程A仍執(zhí)行了new
          Singleton()的步驟3,此時(shí)創(chuàng)建了2個(gè)Singleton對(duì)象,不符合單例模式。

          靜態(tài)內(nèi)部類單例模式:




          /** * 靜態(tài)內(nèi)部類單例模式. * * @author jialin.li * @date 2019-12-30 22:13 */ public class
          Singleton {private Singleton() { } public static Singleton getInstance() {
          return Inner.singleton; } private static class Inner { private static Singleton
          singleton =new Singleton(); } }
            這里利用的是內(nèi)部類的特性,只有第一次調(diào)用getInstance方法的時(shí)候,虛擬機(jī)才會(huì)加載Inner并初始化singleton,并且只初始化一次。這種方法也是一種懶漢式的寫法,只有在需要的時(shí)候,才創(chuàng)建對(duì)象。
          枚舉單例模式
            枚舉類也是一種單例模式 public enum Singleton { INSTANCE //doSomething 該實(shí)例支持的行為 //
          可以省略此方法,通過Singleton.INSTANCE進(jìn)行操作 public static Singleton get Instance() { return
          Singleton.INSTANCE; } } 這種寫法較為簡(jiǎn)單,并且沒有辦法用反射的方式,創(chuàng)建對(duì)象。但是可讀性較差。


          期待您的關(guān)注、推薦、收藏,同時(shí)也期待您的糾錯(cuò)和批評(píng)。

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務(wù)器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點(diǎn)信息
          問題反饋
          郵箱:[email protected]
          QQ群:637538335
          關(guān)注微信

                青青草亚洲 | 极品最强网红八月未央 | 三级片视频网站 | 波多野成人无码精品视频 | youjizz少妇 |