<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      DCL(Double-checked Locking雙重校驗(yàn)鎖)實(shí)現(xiàn)單例模式的原理、問題與解決方案

      ?
      好的,要深入理解DCL(Double-Checked Locking)雙重校驗(yàn)鎖的原理、問題以及解決方法。
      首先,我需要回憶一下單例模式的基本概念,因?yàn)镈CL通常用于實(shí)現(xiàn)單例模式。
      單例模式確保一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)全局訪問點(diǎn)。
      
      接下來,DCL的目的是為了減少同步的開銷。通常,如果直接在getInstance方法上加synchronized關(guān)鍵字,
      雖然能保證線程安全,但每次獲取實(shí)例時(shí)都需要同步,這在多線程環(huán)境下可能會(huì)影響性能。
      而DCL通過兩次檢查實(shí)例是否已經(jīng)創(chuàng)建,來避免每次調(diào)用都進(jìn)行同步,只在第一次創(chuàng)建實(shí)例的時(shí)候同步。
      
      現(xiàn)在,我需要想一下DCL的標(biāo)準(zhǔn)寫法。大概是這樣的:
      
      public class Singleton {
          private static Singleton instance; // 必須使用 volatile
          
          private Singleton() {} // 私有構(gòu)造函數(shù)
          
          public static Singleton getInstance() {
              if (instance == null) {                 // 第一次檢查(避免不必要的同步)
                  synchronized (Singleton.class) {    // 同步塊
                      if (instance == null) {         // 第二次檢查(確保唯一實(shí)例)
                          instance = new Singleton(); // 實(shí)例化
                      }
                  }
              }
              return instance;
          }
      }
      
      
      不過,這種寫法在早期的Java中存在問題,主要涉及到指令重排序的問題。
      因?yàn)閕nstance = new Singleton(); 這個(gè)操作并不是原子性的,它分為三個(gè)步驟:
      
      1. 分配內(nèi)存空間
      2. 初始化對(duì)象
      3. 將instance指向分配的內(nèi)存地址
      
      但由于指令重排序的可能,步驟2和步驟3可能會(huì)被顛倒,導(dǎo)致其他線程在第一次檢查時(shí)看到instance不為null,但實(shí)際上對(duì)象還沒有初始化完成,從而訪問到一個(gè)不完整的對(duì)象。
      
      為了解決這個(gè)問題,Java 5之后引入了volatile關(guān)鍵字,可以禁止指令重排序,
      確保寫操作之前的操作不會(huì)被重排序到寫操作之后。因此,正確的DCL實(shí)現(xiàn)需要將instance聲明為volatile:
      
      private static volatile Singleton instance;
      
      這樣,使用volatile修飾instance變量,就能保證在instance被初始化完成之后,
      其他線程才能看到它的值,從而避免了指令重排序帶來的問題。
      不過,需要確認(rèn)一下volatile在Java內(nèi)存模型中的具體作用。volatile關(guān)鍵字主要有兩個(gè)作用:
      
      1. 可見性:保證變量的修改對(duì)所有線程立即可見。
      2. 禁止指令重排序:通過插入內(nèi)存屏障,確保編譯器和處理器不會(huì)對(duì)指令進(jìn)行重排序。
      
      在DCL中,主要是利用了volatile的第二個(gè)特性,即禁止指令重排序,確保對(duì)象的初始化在賦值操作之后完成。
      另外,我還需要思考其他實(shí)現(xiàn)單例模式的方式,
      比如靜態(tài)內(nèi)部類的方式,或者枚舉方式,這些方式是否更簡(jiǎn)單且線程安全。
      
      靜態(tài)內(nèi)部類的實(shí)現(xiàn)方式:
      public class Singleton {
          private Singleton() {}
      
          private static class Holder {
              private static final Singleton INSTANCE = new Singleton();
          }
          public static Singleton getInstance() {
              return Holder.INSTANCE; // 類加載時(shí)初始化,由 JVM 保證線程安全
          }
      }
      
      這種方式利用了類加載機(jī)制,保證在首次訪問Holder.INSTANCE時(shí)才會(huì)加載Holder類,
      從而初始化Singleton實(shí)例,由JVM保證線程安全。
      這種方法不需要顯式的同步,且實(shí)現(xiàn)簡(jiǎn)單,但可能無法應(yīng)對(duì)反射攻擊或者序列化的問題。
      
      枚舉方式:
      public enum Singleton {
          INSTANCE;
      }
      
      這種方式由JVM保證實(shí)例的唯一性和線程安全,且能防止反射和序列化破壞單例,是最推薦的方式,
      但可能不夠靈活,比如無法繼承其他類。
      
      回到DCL,雖然它在某些情況下仍然有用,但需要正確使用volatile關(guān)鍵字。
      還需要驗(yàn)證一下如果不使用volatile是否一定會(huì)出現(xiàn)問題,以及這種問題發(fā)生的條件。
      
      比如,在Java 5之前,DCL確實(shí)是不安全的,因?yàn)関olatile關(guān)鍵字的語義不夠嚴(yán)格,
      無法完全禁止指令重排序。而在Java 5之后,JSR-133增強(qiáng)了volatile的內(nèi)存語義,確保了DCL的正確性。
      
      另外,我還需要了解內(nèi)存屏障的概念,以及volatile如何通過內(nèi)存屏障來禁止重排序。
      比如,在寫操作之后插入StoreStore屏障,防止寫操作之前的指令被重排序到寫操作之后;
      在讀操作之前插入LoadLoad屏障,防止讀操作之后的指令被重排序到讀操作之前。
      
      總結(jié)一下,DCL的正確實(shí)現(xiàn)需要注意以下幾點(diǎn):
      
      1. 使用雙重檢查來減少同步的開銷。
      2. 使用volatile關(guān)鍵字來禁止指令重排序,保證實(shí)例的可見性和初始化順序。
      3. 確保構(gòu)造函數(shù)是私有的,防止外部實(shí)例化。
      
      可能的誤區(qū)是認(rèn)為只要兩次檢查就能保證線程安全,而忽略了指令重排序的問題。
      因此,volatile的使用是必不可少的。
      
      最后,我需要思考在實(shí)際開發(fā)中,是否應(yīng)該優(yōu)先選擇其他單例實(shí)現(xiàn)方式,
      如枚舉或靜態(tài)內(nèi)部類,而不是DCL,以避免潛在的復(fù)雜性和錯(cuò)誤。
      但在某些需要延遲初始化且對(duì)性能有較高要求的場(chǎng)景下,DCL仍然是一個(gè)合理的選擇,只要正確實(shí)現(xiàn)。

       

       

      前言:什么是單例模式

              單例即單實(shí)例,只實(shí)例出來一個(gè)對(duì)象。

              一般在創(chuàng)建一些管理器類工具類的時(shí)候,需要用到單例模式,比如JDBCUtil 類,我們只需要一個(gè)實(shí)例即可(多個(gè)實(shí)例也可以實(shí)現(xiàn)功能,但是增加了代碼量且降低了性能)。

      如何實(shí)現(xiàn)單例:

      • 構(gòu)造方法私有化
      • 提供一個(gè)全局唯一獲取該類實(shí)例的方法幫助用戶獲取類的實(shí)例

      應(yīng)用場(chǎng)景:

              主要被用于一個(gè)全局類的對(duì)象在多個(gè)地方被使用并且對(duì)象的狀態(tài)是全局變化的場(chǎng)景下。

      單例模式的優(yōu)點(diǎn):

              單例模式為系統(tǒng)資源的優(yōu)化提供了很好的思路,頻繁創(chuàng)建和銷毀對(duì)象都會(huì)增加系統(tǒng)的資源消耗,而單例模式保障了整個(gè)系統(tǒng)只有一個(gè)對(duì)象能被使用,很好地節(jié)約了資源。


      單例模式的四類寫法:

      • 餓漢模式
      • 懶漢模式
      • 靜態(tài)內(nèi)部類
      • 雙重校驗(yàn)鎖

      在講雙重校驗(yàn)鎖之前先來看一下其他模式

      餓漢模式
      顧名思義,餓漢模式就是加載類的時(shí)候直接new一個(gè)對(duì)象,后面直接用即可。

      餓漢模式指在類中直接定義全局的靜態(tài)對(duì)象的實(shí)例初始化,然后提供一個(gè)方法獲取該實(shí)例對(duì)象。

      public class Singleton {
          // 使用static修飾,類加載的時(shí)候new一個(gè)對(duì)象
            private static Singleton INSTANCE = new Singleton();
        
            // 構(gòu)造器私有化
            private Singleton() {}
            
            public static Singleton getInstance() {
                return INSTANCE;
          }
      }


      懶漢模式
      顧名思義,懶漢模式就是加載類的時(shí)候只聲明變量,不new對(duì)象,后面用到的時(shí)候再new對(duì)象,然后把對(duì)象賦給該變量。

      定義一個(gè)私有的靜態(tài)對(duì)象INSTANCE,之所以定義INSTANCE為靜態(tài),是因?yàn)?strong>靜態(tài)屬性或方法是屬于類的,能夠很好地保障單例對(duì)象的唯一性;

      然后定義一個(gè)靜態(tài)方法獲取該對(duì)象,如果對(duì)象為null,則 new 一個(gè)對(duì)象并將其賦值給INSTANCE。

      public class Singleton {
          
            private static Singleton INSTANCE;
        
            // 構(gòu)造器私有化
            private Singleton() {}
            
            public static Singleton getInstance() {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
              }
                return INSTANCE;
          }
      }


      餓漢模式和懶漢模式的區(qū)別在于:

      漢模式是在類加載時(shí)將其實(shí)例化的,在餓漢模式下,在Class Loader完成后該類的實(shí)例便已經(jīng)存在于JVM中了,即,在getInstance方法第一次被調(diào)用前該實(shí)例已經(jīng)存在了,new對(duì)象的操作不在getInstance方法內(nèi)。
      漢模式在類中只是定義了變量但是并未實(shí)例化,實(shí)例化的過程是在獲取單例對(duì)象的方法中實(shí)現(xiàn)的,即,在getInstance方法第一次被調(diào)用后該實(shí)例才會(huì)被創(chuàng)建,new對(duì)象的操作在getInstance方法內(nèi)。
      此外注意:

      餓漢模式的實(shí)例在類加載的時(shí)候已經(jīng)存在于JVM中了,因此是線程安全的

      懶漢模式通過第一次調(diào)用getInstance才實(shí)例化,該方法不是線程安全的(后面講怎么優(yōu)化)

      靜態(tài)內(nèi)部類
      靜態(tài)內(nèi)部類通過在類中定義一個(gè)靜態(tài)內(nèi)部類,將對(duì)象實(shí)例的定義和初始化放在內(nèi)部類中完成,我們?cè)讷@取對(duì)象時(shí)要通過靜態(tài)內(nèi)部類調(diào)用其單例對(duì)象。

      之所以這樣設(shè)計(jì),是因?yàn)轭惖撵o態(tài)內(nèi)部類在JVM中是唯一的,這就很好地保障了單例對(duì)象的唯一性。
      靜態(tài)內(nèi)部類的單例實(shí)現(xiàn)方式同樣是線程安全的。

      代碼如下:

      public class Singleton {
        
            private static class SingletonHolder {
                private static final Singleton INSTANCE = new Singleton();
          }
        
            private Singleton(){}
        
            public static final Singleton getInstance(){
                return SingletonHolder.INSTANCE;
          }
      }


      餓漢模式和靜態(tài)內(nèi)部類實(shí)現(xiàn)單例模式的優(yōu)點(diǎn)是寫法簡(jiǎn)單,缺點(diǎn)是不適合復(fù)雜對(duì)象的創(chuàng)建

      對(duì)于涉及復(fù)雜對(duì)象創(chuàng)建的單例模式,比較優(yōu)雅的實(shí)現(xiàn)方式是懶漢模式,

      但是懶漢模式是非線程安全的,

      下面就講一下懶漢模式的升級(jí)版——DCL雙重構(gòu)校驗(yàn)鎖模式(雙重構(gòu)校驗(yàn)鎖是線程安全的)。

      雙重校驗(yàn)鎖
      餓漢模式是不需要加鎖來保證單例的,而懶漢模式雖然節(jié)省了內(nèi)存,但是卻需要使用鎖來保證單例,因此,雙重校驗(yàn)鎖就是懶漢模式的升級(jí)版本。

      單線程懶漢模式實(shí)現(xiàn)
      普通的懶漢模式在單線程場(chǎng)景下是線程安全的,但在多線程場(chǎng)景下是非線程安全的。

      先來看看普通的懶漢模式實(shí)現(xiàn):

      public class Singleton {
          
            private static Singleton INSTANCE;
        
            private Singleton() {}
            
            public static Singleton getInstance() {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
              }
                return INSTANCE;
          }
      }


      單線程懶漢模式的問題
      上面這段代碼在單線程環(huán)境下沒有問題,但是在多線程的情況下會(huì)產(chǎn)生線程安全問題。

      在多個(gè)線程同時(shí)調(diào)用getInstance方法時(shí),由于方法沒有加鎖,可能會(huì)出現(xiàn)以下情況

      • ① 這些線程可能會(huì)創(chuàng)建多個(gè)對(duì)象
      • ② 某個(gè)線程可能會(huì)得到一個(gè)未完全初始化的對(duì)

      為什么會(huì)出現(xiàn)以上問題?對(duì)于 ① 的情況解釋如下:

      public static Singleton getInstance() {
          if (INSTANCE == null) {
              /**
               * 由于沒有加鎖,當(dāng)線程A剛執(zhí)行完if判斷INSTANCE為null后還沒來得及執(zhí)行INSTANCE = new Singleton()
               * 此時(shí)線程B進(jìn)來,if判斷后INSTANCE為null,且執(zhí)行完INSTANCE = new Singleton()
               * 然后,線程A接著執(zhí)行,由于之前if判斷INSTANCE為null,于是執(zhí)行INSTANCE = new Singleton()重復(fù)創(chuàng)建了對(duì)象
               */
              INSTANCE = new Singleton();
          }
          return INSTANCE;
      }
      

      對(duì)于 ② 的情況解釋如下:

      public static Singleton getInstance() {
          if (INSTANCE == null) {
              /**
               * 由于沒有加鎖,當(dāng)線程A剛執(zhí)行完if判斷INSTANCE為null后開始執(zhí)行 INSTANCE = new Singleton()
               * 但是注意,new Singleton()這個(gè)操作在JVM層面不是一個(gè)原子操作
               *
               *(具體由三步組成:1.為INSTANCE分配內(nèi)存空間;2.初始化INSTANCE;3.將INSTANCE指向分配的內(nèi)存空間,
               * 且這三步在JVM層面有可能發(fā)生指令重排,導(dǎo)致實(shí)際執(zhí)行順序可能為1-3-2)
               *
               * 因?yàn)閚ew操作不是原子化操作,因此,可能會(huì)出現(xiàn)線程A執(zhí)行new Singleton()時(shí)發(fā)生指令重排的情況,
               * 導(dǎo)致實(shí)際執(zhí)行順序變?yōu)?-3-2,當(dāng)執(zhí)行完1-3還沒來及執(zhí)行2時(shí)(雖然還沒執(zhí)行2,但是對(duì)象的引用已經(jīng)有了,
               * 只不過引用的是一個(gè)還沒初始化的對(duì)象),此時(shí)線程B進(jìn)來進(jìn)行if判斷后INSTANCE不為null,
               * 然后直接把線程A new到一半的對(duì)象返回了
               */
              INSTANCE = new Singleton();
          }
          return INSTANCE;
      }
      

      解決問題:加鎖

      為了解決問題 ①,我們可以對(duì) getInstance() 這個(gè)方法加鎖。

      public class Singleton {
        	private static Singleton INSTANCE;
        
        	private Singleton() {}
        	
        	public static synchronized Singleton getInstance() {  // 加鎖
            	if (INSTANCE == null) {
                	INSTANCE = new Singleton();
              }
            	return INSTANCE;
          }
      }
      

      仔細(xì)看,這里是粗暴地對(duì)整個(gè) getInstance() 方法加鎖,這樣做代價(jià)很大,因?yàn)椋挥挟?dāng)?shù)谝淮握{(diào)用 getInstance() 時(shí)才需要同步創(chuàng)建對(duì)象,創(chuàng)建之后再次調(diào)用 getInstance() 時(shí)就只是簡(jiǎn)單的返回成員變量,而這里是無需同步的,所以沒必要對(duì)整個(gè)方法加鎖。

      由于同步一個(gè)方法會(huì)降低上百倍甚至更高的性能, 每次調(diào)用獲取和釋放鎖的開銷似乎是可以避免的:一旦初始化完成,獲取和釋放鎖就顯得很不必要。所以可以只對(duì)方法的部分代碼加鎖!!

      public class Lock2Singleton {
        	private static Lock2Singleton INSTANCE;
        
        	private Lock2Singleton() {}
        
        	public static Lock2Singleton getSingleton() {
              // 因?yàn)镮NSTANCE是靜態(tài)變量,所以給Lock2Singleton的Claa對(duì)象上鎖
              synchronized(Lock2Singleton.class) {        // 加 synchronized
                  if (INSTANCE == null) {
                      INSTANCE = new Lock2Singleton();
                  }
              }
            	return INSTANCE;
          }
      }
      

      優(yōu)化后的代碼選擇了對(duì) if (INSTANCE == null) 和 INSTANCE = new Lock2Singleton()加鎖

      這樣,每個(gè)線程進(jìn)到這個(gè)方法中之后先加鎖,這樣就保證了 if (INSTANCE == null) 和 INSTANCE = new Lock2Singleton() 這兩行代碼被同一個(gè)線程執(zhí)行時(shí)不會(huì)有另外一個(gè)線程進(jìn)來,由此保證了創(chuàng)建的對(duì)象是唯一的

       

      對(duì)象的唯一性保證了,也就是解決了問題①,同時(shí)也解決了問題②。
      為什么說也解決了問題②呢?synchronized不是不能禁止指令重排序嗎?
      其實(shí)當(dāng)我們對(duì)INSTANCE == null和INSTANCE = new Lock2Singleton();加鎖時(shí),也就表示只有一個(gè)線程能進(jìn)來,盡管發(fā)生了指令重排序,也只是在持有鎖的期間發(fā)生了指令重排序,當(dāng)該線程創(chuàng)建完對(duì)象釋放鎖時(shí),new出來的已經(jīng)是一個(gè)完整的對(duì)象。

       

      如此,我們仿佛完美地解決了問題 ① 和 ② ,然而你以為這就結(jié)束了嗎?NO!這段代碼從功能層面來講確實(shí)是已經(jīng)結(jié)束了,但是性能方面呢?是不是還有可以優(yōu)化的地方?

       

      答案是:有!!

       

      值得優(yōu)化的地方就在于 synchronized 代碼塊這里。每個(gè)線程進(jìn)來,不管三七二十一,都要先進(jìn)入同步代碼塊再說,如果說現(xiàn)在 INSTANCE 已經(jīng)不為null了,那么,此時(shí)當(dāng)一個(gè)線程進(jìn)來,先獲得鎖,然后才會(huì)執(zhí)行 if 判斷。我們知道加鎖是非常影響效率的,所以,如果 INSTANCE 已經(jīng)不為null,是不是就可以先判斷,再進(jìn)入 synchronized 代碼塊。如下

      public class Lock2Singleton {
      
        	private static Lock2Singleton INSTANCE;
        
        	private Lock2Singleton() {}
        
        	public static Lock2Singleton getSingleton() {
            	if (INSTANCE == null) {                         // 雙重校驗(yàn):第一次校驗(yàn)
                	synchronized(Lock2Singleton.class) {        // 加 synchronized
                    	if (INSTANCE == null) {                 // 雙重校驗(yàn):第二次校驗(yàn)
                        	INSTANCE = new Lock2Singleton();
                      }
                  }
              }
            	return INSTANCE;
          }
      }
      

      在 synchronized 代碼塊之外再加一個(gè) if 判斷,這樣,當(dāng) INSTANCE 已經(jīng)存在時(shí),線程先判斷不為null,然后直接返回,避免了進(jìn)入 synchronized 同步代碼塊

       

      那么可能又有人問,好了,我明白了在 synchronized 代碼塊外加一個(gè) if 判斷,是不是就意味著里面的那個(gè) if 判斷可以去掉?

       

      當(dāng)然不可以!!

       

      如果把里面的 if 判斷去掉,就相當(dāng)于只對(duì) INSTANCE = new Lock2Singleton() 這一行代碼加了個(gè)鎖,只對(duì)一行代碼加鎖,那你豈不是加了個(gè)寂寞(加鎖的目的就是防止在第二個(gè)if判斷和new操作之間有別的線程進(jìn)來!!),結(jié)果還是會(huì)引起問題①。

       

      所以,兩次校驗(yàn),一次都不能少!!

       

      但是,問題又來了,由于我們?cè)谕鈱佑旨恿艘粚觟f (INSTANCE == null)的判斷,導(dǎo)致原本被我們解決的問題② (即指令重排序問題)又出現(xiàn)了!

       

      比如:線程A拿到鎖后剛走到INSTANCE = new Lock2Singleton(),但是還沒執(zhí)行完,因?yàn)閚ew Lock2Singleton()不是原子操作,且發(fā)生了指令重排序,那么此時(shí)INSTANCE就是一個(gè)不完整的對(duì)象,恰巧此時(shí),線程B來到第一個(gè)if (INSTANCE == null)判斷,由于INSTANCE不為null,結(jié)果獲取到一個(gè)不完整的對(duì)象。

       

      那么怎么解決呢?

       

      答案是加 volatile 關(guān)鍵字,volatile可以禁止指令重排序

      public class Lock2Singleton {
        	private volatile static Lock2Singleton INSTANCE;    // 加 volatile
        
        	private Lock2Singleton() {}
        
        	public static Lock2Singleton getSingleton() {
            	if (INSTANCE == null) {                         // 雙重校驗(yàn):第一次校驗(yàn)
                	synchronized(Lock2Singleton.class) {        // 加 synchronized
                    	if (INSTANCE == null) {                 // 雙重校驗(yàn):第二次校驗(yàn)
                        	INSTANCE = new Lock2Singleton();
                      }
                  }
              }
            	return INSTANCE;
          }
      }
      

       

      一、DCL 的基本實(shí)現(xiàn)

      DCL(Double-Checked Locking)旨在減少同步開銷,僅在首次創(chuàng)建實(shí)例時(shí)使用同步,同時(shí)保證線程安全。

      標(biāo)準(zhǔn)代碼模板

      public class Singleton {
          private static volatile Singleton instance; // 必須使用 volatile
          
          private Singleton() {} // 私有構(gòu)造函數(shù)
          
          public static Singleton getInstance() {
              if (instance == null) {                 // 第一次檢查(避免不必要的同步)
                  synchronized (Singleton.class) {    // 同步塊
                      if (instance == null) {         // 第二次檢查(確保唯一實(shí)例)
                          instance = new Singleton(); // 實(shí)例化
                      }
                  }
              }
              return instance;
          }
      }

      二、DCL 的核心問題

      1. 指令重排序問題

      • 實(shí)例化操作的非原子性
        instance = new Singleton() 分解為三步:

        1. 分配內(nèi)存空間

        2. 初始化對(duì)象

        3. 將引用指向內(nèi)存地址

      • 可能的指令重排序
        若步驟2和3被重排序,其他線程可能訪問到未初始化的對(duì)象(導(dǎo)致空指針異常)。

      2. 可見性問題

      未使用 volatile 時(shí),一個(gè)線程的寫操作可能對(duì)其他線程不可見。


      三、解決方案:volatile 關(guān)鍵字

      volatile 的作用

      1. 禁止指令重排序

        • 通過內(nèi)存屏障(Memory Barrier)確保:

          • 操作前的指令不會(huì)被重排序到操作之后。

          • 操作后的指令不會(huì)被重排序到操作之前。

      2. 保證可見性

        • 修改 volatile 變量后,強(qiáng)制刷新到主內(nèi)存

        • 其他線程讀取時(shí)直接從主內(nèi)存加載。


      四、DCL 的演進(jìn)與 JVM 版本兼容性

      Java 版本DCL 安全性原因
      Java 1.4 及之前 不安全 volatile 語義不完整
      Java 5(JSR-133)及之后 安全 volatile 增強(qiáng)內(nèi)存屏障語義

      五、替代單例實(shí)現(xiàn)方案

      1. 靜態(tài)內(nèi)部類(Holder 模式)

      public class Singleton {
          private Singleton() {}
          
          private static class Holder {
              private static final Singleton INSTANCE = new Singleton();
          }
          
          public static Singleton getInstance() {
              return Holder.INSTANCE; // 類加載時(shí)初始化,由 JVM 保證線程安全
          }
      }
      • 優(yōu)點(diǎn):無鎖、線程安全、延遲加載。

      • 缺點(diǎn):無法防止反射或反序列化破壞單例。

      2. 枚舉單例(推薦)

      public enum Singleton {
          INSTANCE; // 由 JVM 保證唯一性
          
          public void doSomething() {
              // 方法實(shí)現(xiàn)
          }
      }
      • 優(yōu)點(diǎn)

        • 線程安全。

        • 天然防反射和反序列化破壞。

      • 缺點(diǎn):無法繼承其他類。


      六、DCL 的正確使用場(chǎng)景

      • 延遲初始化:僅在需要時(shí)創(chuàng)建實(shí)例。

      • 性能敏感:避免每次調(diào)用同步的開銷。

      • 兼容性要求:需支持 Java 5 及以上版本。


      七、常見誤區(qū)與驗(yàn)證

      1. 錯(cuò)誤:省略 volatile

      private static Singleton instance; // 缺少 volatile
      • 后果:可能返回未完全初始化的對(duì)象(指令重排序?qū)е拢?/p>

      2. 錯(cuò)誤:?jiǎn)未螜z查

      public static Singleton getInstance() {
          if (instance == null) { // 單次檢查
              synchronized (Singleton.class) {
                  instance = new Singleton();
              }
          }
          return instance;
      }
      • 后果:多線程環(huán)境下可能創(chuàng)建多個(gè)實(shí)例。


      八、內(nèi)存屏障與 JVM 實(shí)現(xiàn)細(xì)節(jié)

      • 寫操作屏障:

        • StoreStore 屏障:禁止普通寫與 volatile 寫重排序。

        • StoreLoad 屏障:強(qiáng)制刷新寫緩存到主內(nèi)存。

      • 讀操作屏障:

        • LoadLoad 屏障:禁止 volatile 讀與后續(xù)普通讀重排序。

        • LoadStore 屏障:禁止 volatile 讀與后續(xù)普通寫重排序。


      九、總結(jié)

      • DCL 要點(diǎn)

        • 雙重檢查減少同步開銷。

        • volatile 禁止指令重排序,保證可見性。

      • 適用場(chǎng)景:需要延遲初始化且對(duì)性能有要求的單例實(shí)現(xiàn)。

      • 替代方案:優(yōu)先考慮枚舉或靜態(tài)內(nèi)部類實(shí)現(xiàn)單例。

      正確實(shí)現(xiàn) DCL 需嚴(yán)格遵循代碼模板,避免遺漏 volatile 關(guān)鍵字,以確保線程安全和對(duì)象初始化的正確性。

      ?
      posted @ 2025-03-18 14:18  忙忙碌碌但一無所獲  閱讀(533)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 日韩在线观看精品亚洲| 巨胸美乳无码人妻视频漫画| 99久久精品久久久久久婷婷| 日日碰狠狠添天天爽五月婷| 开心五月婷婷综合网站| 国偷自产一区二区三区在线视频| 西峡县| 欧美粗大猛烈老熟妇| 国产乱码1卡二卡3卡四卡5| 亚洲日本va午夜在线电影| 日本韩国一区二区精品| 亚洲av色综合久久综合| 无码伊人久久大杳蕉中文无码 | 国产精品沙发午睡系列990531| 男女性高爱潮免费网站| 国产精品无码专区| 无码一区二区波多野结衣播放搜索| 国产精品天天看天天狠| 亚洲人成日韩中文字幕不卡| 国产精品区一区第一页| 免费观看日本污污ww网站69| 超碰人人超碰人人| 激情一区二区三区成人文| 中文字幕v亚洲日本在线电影 | 99精品高清在线播放| 国产美女被遭强高潮免费一视频| 亚洲国产成人无码av在线播放| 人妻精品动漫H无码中字| 成年女人免费视频播放体验区| 亚洲一区二区色情苍井空| 国产极品丝尤物在线观看| 国产99在线 | 免费| 三上悠亚精品一区二区久久| 欧美做受视频播放| 丰满老熟妇好大bbbbb| 亚洲欧美日韩在线码| 国产一区二区精品偷系列| 亚洲区1区3区4区中文字幕码| 日韩高清亚洲日韩精品一区二区| 国产国产人免费人成免费| 亚洲最大成人网色|