<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    【轉(zhuǎn)】單例模式:不可使用雙重檢查鎖定

    單例創(chuàng)建模式是一個(gè)通用的編程習(xí)語。和多線程一起使用時(shí),必需使用某種類型的同步。在努力創(chuàng)建更有效的代碼時(shí),Java 程序員們創(chuàng)建了雙重檢查鎖定習(xí)語,將其和單例創(chuàng)建模式一起使用,從而限制同步代碼量。然而,由于一些不太常見的 Java 內(nèi)存模型細(xì)節(jié)的原因,并不能保證這個(gè)雙重檢查鎖定習(xí)語有效。它偶爾會(huì)失敗,而不是總失敗。此外,它失敗的原因并不明顯,還包含 Java 內(nèi)存模型的一些隱秘細(xì)節(jié)。這些事實(shí)將導(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è)計(jì)確保只創(chuàng)建一個(gè) Singleton 對象。構(gòu)造函數(shù)被聲明為 privategetInstance() 方法只創(chuàng)建一個(gè)對象。這個(gè)實(shí)現(xiàn)適合于單線程程序。然而,當(dāng)引入多線程時(shí),就必須通過同步來保護(hù) getInstance() 方法。如果不保護(hù) getInstance() 方法,則可能返回 Singleton 對象的兩個(gè)不同的實(shí)例。假設(shè)兩個(gè)線程并發(fā)調(diào)用 getInstance() 方法并且按以下順序執(zhí)行調(diào)用:

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

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

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

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

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

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

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

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

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


    清單 2. 線程安全的 getInstance() 方法
                            public static synchronized Singleton getInstance()
                {
                if (instance == null)          //1
                instance = new Singleton();  //2
                return instance;               //3
                }
                

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

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

    因?yàn)橹挥星鍐?2 中的 //2 行需要同步,我們可以只將其包裝到一個(gè)同步塊中,如清單 3 所示:


    清單 3. getInstance() 方法
                            public static Singleton getInstance()
                {
                if (instance == null)
                {
                synchronized(Singleton.class) {
                instance = new Singleton();
                }
                }
                return instance;
                }
                

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







    雙重檢查鎖定

    為處理清單 3 中的問題,我們需要對 instance 進(jìn)行第二次檢查。這就是“雙重檢查鎖定”名稱的由來。將雙重檢查鎖定習(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)建兩個(gè)不同的 Singleton 對象成為不可能。假設(shè)有下列事件序列:

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

    2. 由于 instancenull,線程 1 在 //1 處進(jìn)入 synchronized 塊。

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

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

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

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

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

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

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

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

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

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

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







    無序?qū)懭?/span>

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

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

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

    2. 由于 instancenull,線程 1 在 //1 處進(jìn)入 synchronized 塊。

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

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

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

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

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

    此事件序列發(fā)生在線程 2 返回一個(gè)尚未執(zhí)行構(gòu)造函數(shù)的對象的時(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 編譯器上真實(shí)發(fā)生的。執(zhí)行的順序是顛倒的,但鑒于當(dāng)前的內(nèi)存模型,這也是允許發(fā)生的。JIT 編譯器的這一行為使雙重檢查鎖定的問題只不過是一次學(xué)術(shù)實(shí)踐而已。

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


    清單 5. 用于演示無序?qū)懭氲膯卫?/strong>
                            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
                

    注: 為引用下列說明中的匯編代碼行,我將引用指令地址的最后兩個(gè)值,因?yàn)樗鼈兌家?054D20 開頭。例如,B5 代表 test eax,eax

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

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

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







    雙重檢查鎖定:獲取兩個(gè)

    考慮到當(dāng)前的雙重檢查鎖定不起作用,我加入了另一個(gè)版本的代碼,如清單 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)該意識到事情變得有點(diǎn)荒謬。請記住,創(chuàng)建雙重檢查鎖定是為了避免對簡單的三行 getInstance() 方法實(shí)現(xiàn)同步。清單 7 中的代碼變得難于控制。另外,該代碼沒有解決問題。仔細(xì)檢查可獲悉原因。

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

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

    2. 由于 instancenull,線程 1 在 //1 處進(jìn)入第一個(gè) synchronized 塊。

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

    4. 由于 instnull,線程 1 在 //3 處進(jìn)入第二個(gè) synchronized 塊。

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

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

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

    8. 由于 instancenull,線程 2 試圖在 //1 處進(jìn)入第一個(gè) synchronized 塊。由于線程 1 目前持有此鎖,線程 2 被阻斷。

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

    10. 線程 1 然后將一個(gè)構(gòu)造完整的 Singleton 對象在 //5 處賦值給變量 instance,并退出這兩個(gè) 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è)構(gòu)造完整的 Singleton 對象。該問題發(fā)生在理論和實(shí)際彼此背道而馳的情況下。

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

    JIT 編譯器會(huì)在這里看到一個(gè)優(yōu)化的機(jī)會(huì)。此優(yōu)化會(huì)刪除 //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;
                }
                

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







    用 volatile 聲明每一個(gè)變量怎么樣?

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

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

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

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







    解決方案

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

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

    • 放棄同步,而使用一個(gè) static 字段。

    選擇項(xiàng) 2 如清單 10 中所示


    清單 10. 使用 static 字段的單例實(shí)現(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() 方法時(shí)才創(chuàng)建 Singleton。如果您的目標(biāo)是消除同步,則這將是一個(gè)很好的選擇。







    String 不是不變的

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

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

    String 類應(yīng)該是不變的。盡管如此,鑒于我們之前討論的無序?qū)懭雴栴},那會(huì)在這里導(dǎo)致問題嗎?答案是肯定的。考慮兩個(gè)線程訪問 String str。一個(gè)線程能看見 str 引用一個(gè) String 對象,在該對象中構(gòu)造函數(shù)尚未運(yùn)行。事實(shí)上,清單 11 包含展示這種情況發(fā)生的代碼。注意,這個(gè)代碼僅在我測試用的舊版 JVM 上會(huì)失敗。IBM 1.3 和 Sun 1.3 JVM 都會(huì)如期生成不變的 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)建一個(gè) MutableString 類,它包含了一個(gè) String 引用,此引用由 //3 處的兩個(gè)線程共享。在行 //5 和 //6 處,在兩個(gè)分開的線程上創(chuàng)建了兩個(gè)對象 StringCreatorStringReader。傳入一個(gè) MutableString 對象的引用。StringCreator 類進(jìn)入到一個(gè)無限循環(huán)中并且使用值“hello”在 //1 處創(chuàng)建 String 對象。StringReader 也進(jìn)入到一個(gè)無限循環(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 上運(yùn)行此代碼會(huì)導(dǎo)致無序?qū)懭雴栴}。并因此導(dǎo)致一個(gè)非不變的 String







    結(jié)束語

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

    posted on 2011-05-08 19:17 胡鵬 閱讀(344) 評論(0)  編輯  收藏 所屬分類: 設(shè)計(jì)模式


    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     

    導(dǎo)航

    <2011年5月>
    24252627282930
    1234567
    891011121314
    15161718192021
    22232425262728
    2930311234

    統(tǒng)計(jì)

    • 隨筆 - 93
    • 文章 - 0
    • 評論 - 48
    • 引用 - 0

    常用鏈接

    留言簿(3)

    隨筆分類

    隨筆檔案

    agile

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲精品人成无码中文毛片| 亚洲中文字幕精品久久| 国产免费午夜a无码v视频| 久久99青青精品免费观看| 成年网站免费入口在线观看| 亚洲乱码在线观看| 777亚洲精品乱码久久久久久| 国产亚洲情侣一区二区无| 在线观看免费亚洲| 成年美女黄网站18禁免费| 亚洲一级免费视频| 久久久精品免费视频| 性生大片视频免费观看一级| 亚洲欧美成aⅴ人在线观看| 亚洲精品免费在线视频| 亚洲三级电影网址| 亚洲啪啪AV无码片| 中文字幕亚洲图片| 国产成人精品日本亚洲专区| 男人的天堂亚洲一区二区三区| 久9热免费精品视频在线观看| xxxxx做受大片在线观看免费| 日本亚洲高清乱码中文在线观看| 亚洲一区二区三区写真| 2020亚洲男人天堂精品| 亚洲成人免费在线观看| 亚洲成人福利网站| 亚洲欧洲另类春色校园小说| 亚洲成人动漫在线观看| 亚洲国产美女在线观看| 亚洲不卡视频在线观看| 亚洲一区二区三区免费观看| 亚洲国产av一区二区三区丶| 亚洲a级在线观看| 99久久国产亚洲综合精品| 亚洲精品自偷自拍无码| 午夜在线亚洲男人午在线| 色窝窝亚洲av网| 一区二区免费电影| 中文字幕免费在线播放| 免费无码又爽又刺激网站|