級別:中級PeterHaggar,高級軟件工程師,IBM2004年5月01日所有的編程語言都有一些共用的習(xí)語。了解和使用一些習(xí)語很有用,程序員們花費寶貴的時間來創(chuàng)建、學(xué)習(xí)和實現(xiàn)這些習(xí)語。問題是,稍后經(jīng)過證明,一些習(xí)語并不完全如其所聲稱的那樣,或者僅僅是與描述的功能不符。在Java編程語言中,雙重檢查鎖定就是這樣的一個絕不應(yīng)該使用的習(xí)語。在本文中,PeterHaggar" />

亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

雙重檢查鎖定及單例模式

系統(tǒng) 2549 0

雙重檢查鎖定及單例模式

全面理解這一失效的編程習(xí)語


<!----><!----><!---->

級別: 中級

Peter Haggar , 高級軟件工程師, IBM

2004 年 5 月 01 日

所 有的編程語言都有一些共用的習(xí)語。了解和使用一些習(xí)語很有用,程序員們花費寶貴的時間來創(chuàng)建、學(xué)習(xí)和實現(xiàn)這些習(xí)語。問題是,稍后經(jīng)過證明,一些習(xí)語并不完 全如其所聲稱的那樣,或者僅僅是與描述的功能不符。在 Java 編程語言中,雙重檢查鎖定就是這樣的一個絕不應(yīng)該使用的習(xí)語。在本文中,Peter Haggar 介紹了雙重檢查鎖定習(xí)語的淵源,開發(fā)它的原因和它失效的原因。
<!----><!----> <!---->

編輯注 :本文在針對 Java 5.0 修訂前參考了 Java 內(nèi)存模型;關(guān)于內(nèi)存排序的描述也許不再正確。盡管如此,在新的內(nèi)存模型中,雙重檢查鎖定習(xí)語仍舊是無效的。

單 例創(chuàng)建模式是一個通用的編程習(xí)語。和多線程一起使用時,必需使用某種類型的同步。在努力創(chuàng)建更有效的代碼時,Java 程序員們創(chuàng)建了雙重檢查鎖定習(xí)語,將其和單例創(chuàng)建模式一起使用,從而限制同步代碼量。然而,由于一些不太常見的 Java 內(nèi)存模型細(xì)節(jié)的原因,并不能保證這個雙重檢查鎖定習(xí)語有效。它偶爾會失敗,而不是總失敗。此外,它失敗的原因并不明顯,還包含 Java 內(nèi)存模型的一些隱秘細(xì)節(jié)。這些事實將導(dǎo)致代碼失敗,原因是雙重檢查鎖定難于跟蹤。在本文余下的部分里,我們將詳細(xì)介紹雙重檢查鎖定習(xí)語,從而理解它在何處 失效。

單例創(chuàng)建習(xí)語

要理解雙重檢查鎖定習(xí)語是從哪里起源的,就必須理解通用單例創(chuàng)建習(xí)語,如清單 1 中的闡釋:


清單 1. 單例創(chuàng)建習(xí)語
                    				

import java.util.*;
class Singleton
{
  private static Singleton instance;
  private Vector v;
  private boolean inUse;

  private Singleton()
  {
    v = new Vector();
    v.addElement(new Object());
    inUse = true;
  }

  public static Singleton getInstance()
  {
    if (instance == null)          //1
      instance = new Singleton();  //2
    return instance;               //3
  }
}

                  

此類的設(shè)計確保只創(chuàng)建一個 Singleton 對象。構(gòu)造函數(shù)被聲明為 private getInstance() 方法只創(chuàng)建一個對象。這個實現(xiàn)適合于單線程程序。然而,當(dāng)引入多線程時,就必須通過同步來保護 getInstance() 方法。如果不保護 getInstance() 方法,則可能返回 Singleton 對象的兩個不同的實例。假設(shè)兩個線程并發(fā)調(diào)用 getInstance() 方法并且按以下順序執(zhí)行調(diào)用:

  1. 線程 1 調(diào)用 getInstance() 方法并決定 instance 在 //1 處為 null

  2. 線程 1 進入 if 代碼塊,但在執(zhí)行 //2 處的代碼行時被線程 2 預(yù)占。

  3. 線程 2 調(diào)用 getInstance() 方法并在 //1 處決定 instance null

  4. 線程 2 進入 if 代碼塊并創(chuàng)建一個新的 Singleton 對象并在 //2 處將變量 instance 分配給這個新對象。

  5. 線程 2 在 //3 處返回 Singleton 對象引用。

  6. 線程 2 被線程 1 預(yù)占。

  7. 線程 1 在它停止的地方啟動,并執(zhí)行 //2 代碼行,這導(dǎo)致創(chuàng)建另一個 Singleton 對象。

  8. 線程 1 在 //3 處返回這個對象。

結(jié)果是 getInstance() 方法創(chuàng)建了兩個 Singleton 對象,而它本該只創(chuàng)建一個對象。通過同步 getInstance() 方法從而在同一時間只允許一個線程執(zhí)行代碼,這個問題得以改正,如清單 2 所示:


清單 2. 線程安全的 getInstance() 方法
                    				

public static synchronized Singleton getInstance()
{
  if (instance == null)          //1
    instance = new Singleton();  //2
  return instance;               //3
}

                  

清單 2 中的代碼針對多線程訪問 getInstance() 方法運行得很好。然而,當(dāng)分析這段代碼時,您會意識到只有在第一次調(diào)用方法時才需要同步。由于只有第一次調(diào)用執(zhí)行了 //2 處的代碼,而只有此行代碼需要同步,因此就無需對后續(xù)調(diào)用使用同步。所有其他調(diào)用用于決定 instance 是非 null 的,并將其返回。多線程能夠安全并發(fā)地執(zhí)行除第一次調(diào)用外的所有調(diào)用。盡管如此,由于該方法是 synchronized 的,需要為該方法的每一次調(diào)用付出同步的代價,即使只有第一次調(diào)用需要同步。

為使此方法更為有效,一個被稱為雙重檢查鎖定的習(xí)語就應(yīng)運而生了。這個想法是為了避免對除第一次調(diào)用外的所有調(diào)用都實行同步的昂貴代價。同步的代價在不同的 JVM 間是不同的。在早期,代價相當(dāng)高。隨著更高級的 JVM 的出現(xiàn),同步的代價降低了,但出入 synchronized 方法或塊仍然有性能損失。不考慮 JVM 技術(shù)的進步,程序員們絕不想不必要地浪費處理時間。

因為只有清單 2 中的 //2 行需要同步,我們可以只將其包裝到一個同步塊中,如清單 3 所示:


清單 3. getInstance() 方法
                    				

public static Singleton getInstance()
{
  if (instance == null)
  {
    synchronized(Singleton.class) {
      instance = new Singleton();
    }
  }
  return instance;
}

                  

清單 3 中的代碼展示了用多線程加以說明的和清單 1 相同的問題。當(dāng) instance null 時,兩個線程可以并發(fā)地進入 if 語句內(nèi)部。然后,一個線程進入 synchronized 塊來初始化 instance ,而另一個線程則被阻斷。當(dāng)?shù)谝粋€線程退出 synchronized 塊時,等待著的線程進入并創(chuàng)建另一個 Singleton 對象。注意:當(dāng)?shù)诙€線程進入 synchronized 塊時,它并沒有檢查 instance 是否非 null





回頁首


雙重檢查鎖定

為處理清單 3 中的問題,我們需要對 instance 進行第二次檢查。這就是“雙重檢查鎖定”名稱的由來。將雙重檢查鎖定習(xí)語應(yīng)用到清單 3 的結(jié)果就是清單 4 。


清單 4. 雙重檢查鎖定示例
                    				

public static Singleton getInstance()
{
  if (instance == null)
  {
    synchronized(Singleton.class) {  //1
      if (instance == null)          //2
        instance = new Singleton();  //3
    }
  }
  return instance;
}

                  

雙重檢查鎖定背后的理論是:在 //2 處的第二次檢查使(如清單 3 中那樣)創(chuàng)建兩個不同的 Singleton 對象成為不可能。假設(shè)有下列事件序列:

  1. 線程 1 進入 getInstance() 方法。

  2. 由于 instance null ,線程 1 在 //1 處進入 synchronized 塊。

  3. 線程 1 被線程 2 預(yù)占。

  4. 線程 2 進入 getInstance() 方法。

  5. 由于 instance 仍舊為 null ,線程 2 試圖獲取 //1 處的鎖。然而,由于線程 1 持有該鎖,線程 2 在 //1 處阻塞。

  6. 線程 2 被線程 1 預(yù)占。

  7. 線程 1 執(zhí)行,由于在 //2 處實例仍舊為 null ,線程 1 還創(chuàng)建一個 Singleton 對象并將其引用賦值給 instance

  8. 線程 1 退出 synchronized 塊并從 getInstance() 方法返回實例。

  9. 線程 1 被線程 2 預(yù)占。

  10. 線程 2 獲取 //1 處的鎖并檢查 instance 是否為 null

  11. 由于 instance 是非 null 的,并沒有創(chuàng)建第二個 Singleton 對象,由線程 1 創(chuàng)建的對象被返回。

雙重檢查鎖定背后的理論是完美的。不幸地是,現(xiàn)實完全不同。雙重檢查鎖定的問題是:并不能保證它會在單處理器或多處理器計算機上順利運行。

雙重檢查鎖定失敗的問題并不歸咎于 JVM 中的實現(xiàn) bug,而是歸咎于 Java 平臺內(nèi)存模型。內(nèi)存模型允許所謂的“無序?qū)懭搿保@也是這些習(xí)語失敗的一個主要原因。





回頁首


無序?qū)懭?

為解釋該問題,需要重新考察上述清單 4 中的 //3 行。此行代碼創(chuàng)建了一個 Singleton 對象并初始化變量 instance 來引用此對象。這行代碼的問題是:在 Singleton 構(gòu)造函數(shù)體執(zhí)行之前,變量 instance 可能成為非 null 的。

什么?這一說法可能讓您始料未及,但事實確實如此。在解釋這個現(xiàn)象如何發(fā)生前,請先暫時接受這一事實,我們先來考察一下雙重檢查鎖定是如何被破壞的。假設(shè)清單 4 中代碼執(zhí)行以下事件序列:

  1. 線程 1 進入 getInstance() 方法。

  2. 由于 instance null ,線程 1 在 //1 處進入 synchronized 塊。

  3. 線程 1 前進到 //3 處,但在構(gòu)造函數(shù)執(zhí)行 之前 ,使實例成為非 null

  4. 線程 1 被線程 2 預(yù)占。

  5. 線程 2 檢查實例是否為 null 。因為實例不為 null,線程 2 將 instance 引用返回給一個構(gòu)造完整但部分初始化了的 Singleton 對象。

  6. 線程 2 被線程 1 預(yù)占。

  7. 線程 1 通過運行 Singleton 對象的構(gòu)造函數(shù)并將引用返回給它,來完成對該對象的初始化。

此事件序列發(fā)生在線程 2 返回一個尚未執(zhí)行構(gòu)造函數(shù)的對象的時候。

為展示此事件的發(fā)生情況,假設(shè)為代碼行 instance =new Singleton(); 執(zhí)行了下列偽代碼: instance =new Singleton();

                    mem = allocate();             //Allocate memory for Singleton object.
instance = mem;               //Note that instance is now non-null, but
                              //has not been initialized.
ctorSingleton(instance);      //Invoke constructor for Singleton passing
                              //instance.

                  

這段偽代碼不僅是可能的,而且是一些 JIT 編譯器上真實發(fā)生的。執(zhí)行的順序是顛倒的,但鑒于當(dāng)前的內(nèi)存模型,這也是允許發(fā)生的。JIT 編譯器的這一行為使雙重檢查鎖定的問題只不過是一次學(xué)術(shù)實踐而已。

為說明這一情況,假設(shè)有清單 5 中的代碼。它包含一個剝離版的 getInstance() 方法。我已經(jīng)刪除了“雙重檢查性”以簡化我們對生成的匯編代碼(清單 6)的回顧。我們只關(guān)心 JIT 編譯器如何編譯 instance=new Singleton(); 代碼。此外,我提供了一個簡單的構(gòu)造函數(shù)來明確說明匯編代碼中該構(gòu)造函數(shù)的運行情況。


清單 5. 用于演示無序?qū)懭氲膯卫?
                    				

class Singleton
{
  private static Singleton instance;
  private boolean inUse;
  private int val;  

  private Singleton()
  {
    inUse = true;
    val = 5;
  }
  public static Singleton getInstance()
  {
    if (instance == null)
      instance = new Singleton();
    return instance;
  }
}

                  

清單 6 包含由 Sun JDK 1.2.1 JIT 編譯器為清單 5 中的 getInstance() 方法體生成的匯編代碼。


清單 6. 由清單 5 中的代碼生成的匯編代碼
                    				

;asm code generated for getInstance
054D20B0   mov         eax,[049388C8]      ;load instance ref
054D20B5   test        eax,eax             ;test for null
054D20B7   jne         054D20D7
054D20B9   mov         eax,14C0988h
054D20BE   call        503EF8F0            ;allocate memory
054D20C3   mov         [049388C8],eax      ;store pointer in 
                                           ;instance ref. instance  
                                           ;non-null and ctor
                                           ;has not run
054D20C8   mov         ecx,dword ptr [eax] 
054D20CA   mov         dword ptr [ecx],1   ;inline ctor - inUse=true;
054D20D0   mov         dword ptr [ecx+4],5 ;inline ctor - val=5;
054D20D7   mov         ebx,dword ptr ds:[49388C8h]
054D20DD   jmp         054D20B0

                  

注: 為引用下列說明中的匯編代碼行,我將引用指令地址的最后兩個值,因為它們都以 054D20 開頭。例如, B5 代表 test eax,eax

匯編代碼是通過運行一個在無限循環(huán)中調(diào)用 getInstance() 方法的測試程序來生成的。程序運行時,請運行 Microsoft Visual C++ 調(diào)試器并將其附到表示測試程序的 Java 進程中。然后,中斷執(zhí)行并找到表示該無限循環(huán)的匯編代碼。

B0 B5 處的前兩行匯編代碼將 instance 引用從內(nèi)存位置 049388C8 加載至 eax 中,并進行 null 檢查。這跟清單 5 中的 getInstance() 方法的第一行代碼相對應(yīng)。第一次調(diào)用此方法時, instance null ,代碼執(zhí)行到 B9 BE 處的代碼為 Singleton 對象從堆中分配內(nèi)存,并將一個指向該塊內(nèi)存的指針存儲到 eax 中。下一行代碼, C3 ,獲取 eax 中的指針并將其存儲回內(nèi)存位置為 049388C8 的實例引用。結(jié)果是, instance 現(xiàn)在為非 null 并引用一個有效的 Singleton 對象。然而,此對象的構(gòu)造函數(shù)尚未運行,這恰是破壞雙重檢查鎖定的情況。然后,在 C8 行處, instance 指針被解除引用并存儲到 ecx CA D0 行表示內(nèi)聯(lián)的構(gòu)造函數(shù),該構(gòu)造函數(shù)將值 true 5 存儲到 Singleton 對象。如果此代碼在執(zhí)行 C3 行后且在完成該構(gòu)造函數(shù)前被另一個線程中斷,則雙重檢查鎖定就會失敗。

不是所有的 JIT 編譯器都生成如上代碼。一些生成了代碼,從而只在構(gòu)造函數(shù)執(zhí)行后使 instance 成為非 null 。 針對 Java 技術(shù)的 IBM SDK 1.3 版和 Sun JDK 1.3 都生成這樣的代碼。然而,這并不意味著應(yīng)該在這些實例中使用雙重檢查鎖定。該習(xí)語失敗還有一些其他原因。此外,您并不總能知道代碼會在哪些 JVM 上運行,而 JIT 編譯器總是會發(fā)生變化,從而生成破壞此習(xí)語的代碼。





回頁首


雙重檢查鎖定:獲取兩個

考慮到當(dāng)前的雙重檢查鎖定不起作用,我加入了另一個版本的代碼,如清單 7 所示,從而防止您剛才看到的無序?qū)懭雴栴}。


清單 7. 解決無序?qū)懭雴栴}的嘗試
                    				

public static Singleton getInstance()
{
  if (instance == null)
  {
    synchronized(Singleton.class) {      //1
      Singleton inst = instance;         //2
      if (inst == null)
      {
        synchronized(Singleton.class) {  //3
          inst = new Singleton();        //4
        }
        instance = inst;                 //5
      }
    }
  }
  return instance;
}

                  

看著清單 7 中的代碼,您應(yīng)該意識到事情變得有點荒謬。請記住,創(chuàng)建雙重檢查鎖定是為了避免對簡單的三行 getInstance() 方法實現(xiàn)同步。清單 7 中的代碼變得難于控制。另外,該代碼沒有解決問題。仔細(xì)檢查可獲悉原因。

此代碼試圖避免無序?qū)懭雴栴}。它試圖通過引入局部變量 inst 和第二個 synchronized 塊來解決這一問題。該理論實現(xiàn)如下:

  1. 線程 1 進入 getInstance() 方法。

  2. 由于 instance null ,線程 1 在 //1 處進入第一個 synchronized 塊。

  3. 局部變量 inst 獲取 instance 的值,該值在 //2 處為 null

  4. 由于 inst null ,線程 1 在 //3 處進入第二個 synchronized 塊。

  5. 線程 1 然后開始執(zhí)行 //4 處的代碼,同時使 inst 為非 null ,但在 Singleton 的構(gòu)造函數(shù)執(zhí)行前。(這就是我們剛才看到的無序?qū)懭雴栴}。)

  6. 線程 1 被線程 2 預(yù)占。

  7. 線程 2 進入 getInstance() 方法。

  8. 由于 instance null ,線程 2 試圖在 //1 處進入第一個 synchronized 塊。由于線程 1 目前持有此鎖,線程 2 被阻斷。

  9. 線程 1 然后完成 //4 處的執(zhí)行。

  10. 線程 1 然后將一個構(gòu)造完整的 Singleton 對象在 //5 處賦值給變量 instance ,并退出這兩個 synchronized 塊。

  11. 線程 1 返回 instance

  12. 然后執(zhí)行線程 2 并在 //2 處將 instance 賦值給 inst

  13. 線程 2 發(fā)現(xiàn) instance 為非 null ,將其返回。

這里的關(guān)鍵行是 //5。此行應(yīng)該確保 instance 只為 null 或引用一個構(gòu)造完整的 Singleton 對象。該問題發(fā)生在理論和實際彼此背道而馳的情況下。

由于當(dāng)前內(nèi)存模型的定義,清單 7 中的代碼無效。Java 語言規(guī)范(Java Language Specification,JLS)要求不能將 synchronized 塊中的代碼移出來。但是,并沒有說不能將 synchronized 塊外面的代碼移 synchronized 塊中。

JIT 編譯器會在這里看到一個優(yōu)化的機會。此優(yōu)化會刪除 //4 和 //5 處的代碼,組合并且生成清單 8 中所示的代碼。


清單 8. 從清單 7 中優(yōu)化來的代碼。
                    				

public static Singleton getInstance()
{
  if (instance == null)
  {
    synchronized(Singleton.class) {      //1
      Singleton inst = instance;         //2
      if (inst == null)
      {
        synchronized(Singleton.class) {  //3
          //inst = new Singleton();      //4
          instance = new Singleton();               
        }
        //instance = inst;               //5
      }
    }
  }
  return instance;
}

                  

如果進行此項優(yōu)化,您將同樣遇到我們之前討論過的無序?qū)懭雴栴}。





回頁首


用 volatile 聲明每一個變量怎么樣?

另一個想法是針對變量 inst 以及 instance 使用關(guān)鍵字 volatile 。根據(jù) JLS(參見 參考資料 ),聲明成 volatile 的變量被認(rèn)為是順序一致的,即,不是重新排序的。但是試圖使用 volatile 來修正雙重檢查鎖定的問題,會產(chǎn)生以下兩個問題:

  • 這里的問題不是有關(guān)順序一致性的,而是代碼被移動了,不是重新排序。

  • 即使考慮了順序一致性,大多數(shù)的 JVM 也沒有正確地實現(xiàn) volatile

第二點值得展開討論。假設(shè)有清單 9 中的代碼:


清單 9. 使用了 volatile 的順序一致性
                    				

class test
{
  private volatile boolean stop = false;
  private volatile int num = 0;

  public void foo()
  {
    num = 100;    //This can happen second
    stop = true;  //This can happen first
    //...
  }

  public void bar()
  {
    if (stop)
      num += num;  //num can == 0!
  }
  //...
}

                  

根據(jù) JLS,由于 stop num 被聲明為 volatile ,它們應(yīng)該順序一致。這意味著如果 stop 曾經(jīng)是 true num 一定曾被設(shè)置成 100 。盡管如此,因為許多 JVM 沒有實現(xiàn) volatile 的順序一致性功能,您就不能依賴此行為。因此,如果線程 1 調(diào)用 foo 并且線程 2 并發(fā)地調(diào)用 bar ,則線程 1 可能在 num 被設(shè)置成為 100 之前將 stop 設(shè)置成 true 。這將導(dǎo)致線程見到 stop true ,而 num 仍被設(shè)置成 0 。使用 volatile 和 64 位變量的原子數(shù)還有另外一些問題,但這已超出了本文的討論范圍。有關(guān)此主題的更多信息,請參閱 參考資料





回頁首


解決方案

底線就是:無論以何種形式,都不應(yīng)使用雙重檢查鎖定,因為您不能保證它在任何 JVM 實現(xiàn)上都能順利運行。JSR-133 是有關(guān)內(nèi)存模型尋址問題的,盡管如此,新的內(nèi)存模型也不會支持雙重檢查鎖定。因此,您有兩種選擇:

  • 接受如清單 2 中所示的 getInstance() 方法的同步。

  • 放棄同步,而使用一個 static 字段。

選擇項 2 如清單 10 中所示


清單 10. 使用 static 字段的單例實現(xiàn)
                    				

class Singleton
{
  private Vector v;
  private boolean inUse;
  private static Singleton instance = new Singleton();

  private Singleton()
  {
    v = new Vector();
    inUse = true;
    //...
  }

  public static Singleton getInstance()
  {
    return instance;
  }
}

                  

清單 10 的代碼沒有使用同步,并且確保調(diào)用 static getInstance() 方法時才創(chuàng)建 Singleton 。如果您的目標(biāo)是消除同步,則這將是一個很好的選擇。





回頁首


String 不是不變的

鑒于無序?qū)懭牒鸵迷跇?gòu)造函數(shù)執(zhí)行前變成非 null 的問題,您可能會考慮 String 類。假設(shè)有下列代碼:

                    private String str;
//...
str = new String("hello");

                  

String 類應(yīng)該是不變的。盡管如此,鑒于我們之前討論的無序?qū)懭雴栴},那會在這里導(dǎo)致問題嗎?答案是肯定的。考慮兩個線程訪問 String str 。一個線程能看見 str 引用一個 String 對象,在該對象中構(gòu)造函數(shù)尚未運行。事實上,清單 11 包含展示這種情況發(fā)生的代碼。注意,這個代碼僅在我測試用的舊版 JVM 上會失敗。IBM 1.3 和 Sun 1.3 JVM 都會如期生成不變的 String


清單 11. 可變 String 的例子
                    				

class StringCreator extends Thread
{
  MutableString ms;
  public StringCreator(MutableString muts)
  {
    ms = muts;
  }
  public void run()
  {
    while(true)
      ms.str = new String("hello");          //1
  }
}
class StringReader extends Thread
{
  MutableString ms;
  public StringReader(MutableString muts)
  {
    ms = muts;
  }
  public void run()
  {
    while(true)
    {
      if (!(ms.str.equals("hello")))         //2
      {
        System.out.println("String is not immutable!");
        break;
      }
    }
  }
}
class MutableString
{
  public String str;                         //3
  public static void main(String args[])
  {
    MutableString ms = new MutableString();  //4
    new StringCreator(ms).start();           //5
    new StringReader(ms).start();            //6
  }
}

                  

此代碼在 //4 處創(chuàng)建一個 MutableString 類,它包含了一個 String 引用,此引用由 //3 處的兩個線程共享。在行 //5 和 //6 處,在兩個分開的線程上創(chuàng)建了兩個對象 StringCreator StringReader 。傳入一個 MutableString 對象的引用。 StringCreator 類進入到一個無限循環(huán)中并且使用值“hello”在 //1 處創(chuàng)建 String 對象。 StringReader 也進入到一個無限循環(huán)中,并且在 //2 處檢查當(dāng)前的 String 對象的值是不是 “hello”。如果不行, StringReader 線程打印出一條消息并停止。如果 String 類是不變的,則從此程序應(yīng)當(dāng)看不到任何輸出。如果發(fā)生了無序?qū)懭雴栴},則使 StringReader 看到 str 引用的惟一方法絕不是值為“hello”的 String 對象。

在舊版的 JVM 如 Sun JDK 1.2.1 上運行此代碼會導(dǎo)致無序?qū)懭雴栴}。并因此導(dǎo)致一個非不變的 String





回頁首


結(jié)束語

為避免單例中代價高昂的同步,程序員非常聰明地發(fā)明了雙重檢查鎖定習(xí)語。不幸的是,鑒于當(dāng)前的內(nèi)存模型的原因,該習(xí)語尚未得到廣泛使用,就明顯成為了一種 不安全的編程結(jié)構(gòu)。重定義脆弱的內(nèi)存模型這一領(lǐng)域的工作正在進行中。盡管如此,即使是在新提議的內(nèi)存模型中,雙重檢查鎖定也是無效的。對此問題最佳的解決 方案是接受同步或者使用一個 static field



參考資料



關(guān)于作者

Peter Haggar 是 IBM 在北卡羅來納州的 Research Triangle Park 的一名高級軟件工程師,他還是 Practical Java Programming Language Guide (Addison-Wesley 出版)一書的作者。此外,他還發(fā)表了很多篇關(guān)于 Java 編程的文章。他有著廣泛的編程經(jīng)驗,曾致力于開發(fā)工具、類庫和操作系統(tǒng)相關(guān)的工作。Peter 在 IBM 致力于研究新興 Internet 技術(shù),目前主要從事高性能 Web 服務(wù)方面的工作。Peter 經(jīng)常在很多行業(yè)會議上作為技術(shù)發(fā)言人就 Java 技術(shù)發(fā)表言論。他已經(jīng)為 IBM 工作了 14 年多,并獲得了 Clarkson University 的計算機科學(xué)學(xué)士學(xué)位。您可以通過 haggar@us.ibm.com 與他聯(lián)系。

雙重檢查鎖定及單例模式


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 亚洲图片欧美另类 | xxxxxx日本人免费 | 一日本道加勒比高清一二三 | 欧美国产日韩精品 | 国产精品 色 | 成年女人免费看片 | 羞羞的视频网站 | 亚洲精品一区二区三区福利 | 黄黄视频在线观看 | 久久精品在现线观看免费15 | 四虎影院新地址 | 亚洲精品麻豆 | 视色视频在线 | 成人精品第一区二区三区 | 一级白嫩美女毛片免费 | 国产一级持黄大片99久久 | 一区二区三区免费视频 www | 7777精品伊人久久久大香线蕉 | 男人资源网站 | 最近中文字幕在线视频1 | 四虎.com| 国产成人亚洲综合小说区 | 国产成人在线播放 | 日本欧美视频在线 | 久久久久久网站 | 手机看片久久高清国产日韩 | 久久99热只有频精品6不卡 | 亚洲视频播放 | 天天做天天爱夜夜大爽完整 | 九九热在线观看 | 国产在线91精品入口 | 成人免费视频一区二区 | 欧美日韩亚洲国产一区二区综合 | 91精品久久一区二区三区 | 九九视频高清视频免费观看 | 精品亚洲一区二区三区在线播放 | 日本高清视频不卡 | a亚洲欧美中文日韩在线v日本 | 国产91一区二这在线播放 | 91丨九色丨蝌蚪 | 国产精品国内免费一区二区三区 |