亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專(zhuān)欄INFORMATION COLUMN

雙重檢查鎖定與延遲初始化

yvonne / 3198人閱讀

摘要:基于的雙重檢查鎖定的解決方案對(duì)于前面的基于雙重檢查鎖定來(lái)實(shí)現(xiàn)延遲初始化的方案指示例代碼,我們只需要做一點(diǎn)小的修改把聲明為型,就可以實(shí)現(xiàn)線(xiàn)程安全的延遲初始化。

雙重檢查鎖定的由來(lái)

在java程序中,有時(shí)候可能需要推遲一些高開(kāi)銷(xiāo)的對(duì)象初始化操作,并且只有在使用這些對(duì)象時(shí)才進(jìn)行初始化。此時(shí)程序員可能會(huì)采用延遲初始化。但要正確實(shí)現(xiàn)線(xiàn)程安全的延遲初始化需要一些技巧,否則很容易出現(xiàn)問(wèn)題。比如,下面是非線(xiàn)程安全的延遲初始化對(duì)象的示例代碼:

public class UnsafeLazyInitialization {
    private static Instance instance;

    public static Instance getInstance() {
        if (instance == null)          //1:A線(xiàn)程執(zhí)行
            instance = new Instance(); //2:B線(xiàn)程執(zhí)行
        return instance;
    }
}

在UnsafeLazyInitialization中,假設(shè)A線(xiàn)程執(zhí)行代碼1的同時(shí),B線(xiàn)程執(zhí)行代碼2。此時(shí),線(xiàn)程A可能會(huì)看到instance引用的對(duì)象還沒(méi)有完成初始化(出現(xiàn)這種情況的原因見(jiàn)后文的“問(wèn)題的根源”)。

對(duì)于UnsafeLazyInitialization,我們可以對(duì)getInstance()做同步處理來(lái)實(shí)現(xiàn)線(xiàn)程安全的延遲初始化。示例代碼如下:

遲初始化。示例代碼如下:

public class SafeLazyInitialization {
    private static Instance instance;

    public synchronized static Instance getInstance() {
        if (instance == null)
            instance = new Instance();
        return instance;
    }
}

由于對(duì)getInstance()做了同步處理,synchronized將導(dǎo)致性能開(kāi)銷(xiāo)。如果getInstance()被多個(gè)線(xiàn)程頻繁的調(diào)用,將會(huì)導(dǎo)致程序執(zhí)行性能的下降。反之,如果getInstance()不會(huì)被多個(gè)線(xiàn)程頻繁的調(diào)用,那么這個(gè)延遲初始化方案將能提供令人滿(mǎn)意的性能。

在早期的JVM中,synchronized(甚至是無(wú)競(jìng)爭(zhēng)的synchronized)存在這巨大的性能開(kāi)銷(xiāo)。因此,人們想出了一個(gè)“聰明”的技巧:雙重檢查鎖定(double-checked locking)。人們想通過(guò)雙重檢查鎖定來(lái)降低同步的開(kāi)銷(xiāo)。下面是使用雙重檢查鎖定來(lái)實(shí)現(xiàn)延遲初始化的示例代碼:

public class DoubleCheckedLocking {                 //1
    private static Instance instance;                    //2

    public static Instance getInstance() {               //3
        if (instance == null) {                          //4:第一次檢查
            synchronized (DoubleCheckedLocking.class) {  //5:加鎖
                if (instance == null)                    //6:第二次檢查
                    instance = new Instance();           //7:問(wèn)題的根源出在這里
            }                                            //8
        }                                                //9
        return instance;                                 //10
    }                                                    //11
}

如上面代碼所示,如果第一次檢查instance不為null,那么就不需要執(zhí)行下面的加鎖和初始化操作。因此可以大幅降低synchronized帶來(lái)的性能開(kāi)銷(xiāo)。上面代碼表面上看起來(lái),似乎兩全其美:

在多個(gè)線(xiàn)程試圖在同一時(shí)間創(chuàng)建對(duì)象時(shí),會(huì)通過(guò)加鎖來(lái)保證只有一個(gè)線(xiàn)程能創(chuàng)建對(duì)象。

在對(duì)象創(chuàng)建好之后,執(zhí)行g(shù)etInstance()將不需要獲取鎖,直接返回已創(chuàng)建好的對(duì)象。

雙重檢查鎖定看起來(lái)似乎很完美,但這是一個(gè)錯(cuò)誤的優(yōu)化!在線(xiàn)程執(zhí)行到第4行代碼讀取到instance不為null時(shí),instance引用的對(duì)象有可能還沒(méi)有完成初始化。

問(wèn)題的根源

前面的雙重檢查鎖定示例代碼的第7行(instance = new Singleton();)創(chuàng)建一個(gè)對(duì)象。這一行代碼可以分解為如下的三行偽代碼:

memory = allocate();   //1:分配對(duì)象的內(nèi)存空間
ctorInstance(memory);  //2:初始化對(duì)象
instance = memory;     //3:設(shè)置instance指向剛分配的內(nèi)存地址

上面三行偽代碼中的2和3之間,可能會(huì)被重排序(在一些JIT編譯器上,這種重排序是真實(shí)發(fā)生的,詳情見(jiàn)參考文獻(xiàn)1的“Out-of-order writes”部分)。2和3之間重排序之后的執(zhí)行時(shí)序如下:

memory = allocate();   //1:分配對(duì)象的內(nèi)存空間
instance = memory;     //3:設(shè)置instance指向剛分配的內(nèi)存地址
                       //注意,此時(shí)對(duì)象還沒(méi)有被初始化!
ctorInstance(memory);  //2:初始化對(duì)象

根據(jù)《The Java Language Specification, Java SE 7 Edition》(后文簡(jiǎn)稱(chēng)為java語(yǔ)言規(guī)范),所有線(xiàn)程在執(zhí)行java程序時(shí)必須要遵守intra-thread semantics。intra-thread semantics保證重排序不會(huì)改變單線(xiàn)程內(nèi)的程序執(zhí)行結(jié)果。換句話(huà)來(lái)說(shuō),intra-thread semantics允許那些在單線(xiàn)程內(nèi),不會(huì)改變單線(xiàn)程程序執(zhí)行結(jié)果的重排序。上面三行偽代碼的2和3之間雖然被重排序了,但這個(gè)重排序并不會(huì)違反intra-thread semantics。這個(gè)重排序在沒(méi)有改變單線(xiàn)程程序的執(zhí)行結(jié)果的前提下,可以提高程序的執(zhí)行性能。

為了更好的理解intra-thread semantics,請(qǐng)看下面的示意圖(假設(shè)一個(gè)線(xiàn)程A在構(gòu)造對(duì)象后,立即訪(fǎng)問(wèn)這個(gè)對(duì)象):

如上圖所示,只要保證2排在4的前面,即使2和3之間重排序了,也不會(huì)違反intra-thread semantics。

下面,再讓我們看看多線(xiàn)程并發(fā)執(zhí)行的時(shí)候的情況。請(qǐng)看下面的示意圖:

由于單線(xiàn)程內(nèi)要遵守intra-thread semantics,從而能保證A線(xiàn)程的程序執(zhí)行結(jié)果不會(huì)被改變。但是當(dāng)線(xiàn)程A和B按上圖的時(shí)序執(zhí)行時(shí),B線(xiàn)程將看到一個(gè)還沒(méi)有被初始化的對(duì)象。

※注:本文統(tǒng)一用紅色的虛箭線(xiàn)標(biāo)識(shí)錯(cuò)誤的讀操作,用綠色的虛箭線(xiàn)標(biāo)識(shí)正確的讀操作。

回到本文的主題,DoubleCheckedLocking示例代碼的第7行(instance = new Singleton();)如果發(fā)生重排序,另一個(gè)并發(fā)執(zhí)行的線(xiàn)程B就有可能在第4行判斷instance不為null。線(xiàn)程B接下來(lái)將訪(fǎng)問(wèn)instance所引用的對(duì)象,但此時(shí)這個(gè)對(duì)象可能還沒(méi)有被A線(xiàn)程初始化!下面是這個(gè)場(chǎng)景的具體執(zhí)行時(shí)序:

|時(shí)間 | 線(xiàn)程A| 線(xiàn)程B|
|t1| A1:分配對(duì)象的內(nèi)存空間||
|t2| A3:設(shè)置instance指向內(nèi)存空間|
|t3|| B1:判斷instance是否為空|
|t4|| B2:由于instance不為null,線(xiàn)程B將訪(fǎng)問(wèn)instance引用的對(duì)象|
|t5| A2:初始化對(duì)象||
|t6| A4:訪(fǎng)問(wèn)instance引用的對(duì)象||

這里A2和A3雖然重排序了,但java內(nèi)存模型的intra-thread semantics將確保A2一定會(huì)排在A(yíng)4前面執(zhí)行。因此線(xiàn)程A的intra-thread semantics沒(méi)有改變。但A2和A3的重排序,將導(dǎo)致線(xiàn)程B在B1處判斷出instance不為空,線(xiàn)程B接下來(lái)將訪(fǎng)問(wèn)instance引用的對(duì)象。此時(shí),線(xiàn)程B將會(huì)訪(fǎng)問(wèn)到一個(gè)還未初始化的對(duì)象。

在知曉了問(wèn)題發(fā)生的根源之后,我們可以想出兩個(gè)辦法來(lái)實(shí)現(xiàn)線(xiàn)程安全的延遲初始化:

不允許2和3重排序;

允許2和3重排序,但不允許其他線(xiàn)程“看到”這個(gè)重排序。
后文介紹的兩個(gè)解決方案,分別對(duì)應(yīng)于上面這兩點(diǎn)。

基于volatile的雙重檢查鎖定的解決方案

對(duì)于前面的基于雙重檢查鎖定來(lái)實(shí)現(xiàn)延遲初始化的方案(指DoubleCheckedLocking示例代碼),我們只需要做一點(diǎn)小的修改(把instance聲明為volatile型),就可以實(shí)現(xiàn)線(xiàn)程安全的延遲初始化。請(qǐng)看下面的示例代碼:

public class SafeDoubleCheckedLocking {
    private volatile static Instance instance;

    public static Instance getInstance() {
        if (instance == null) {
            synchronized (SafeDoubleCheckedLocking.class) {
                if (instance == null)
                    instance = new Instance();//instance為volatile,現(xiàn)在沒(méi)問(wèn)題了
            }
        }
        return instance;
    }
}

注意,這個(gè)解決方案需要JDK5或更高版本(因?yàn)閺腏DK5開(kāi)始使用新的JSR-133內(nèi)存模型規(guī)范,這個(gè)規(guī)范增強(qiáng)了volatile的語(yǔ)義)。

當(dāng)聲明對(duì)象的引用為volatile后,“問(wèn)題的根源”的三行偽代碼中的2和3之間的重排序,在多線(xiàn)程環(huán)境中將會(huì)被禁止。上面示例代碼將按如下的時(shí)序執(zhí)行:

這個(gè)方案本質(zhì)上是通過(guò)禁止上圖中的2和3之間的重排序,來(lái)保證線(xiàn)程安全的延遲初始化。

基于類(lèi)初始化的解決方案

JVM在類(lèi)的初始化階段(即在Class被加載后,且被線(xiàn)程使用之前),會(huì)執(zhí)行類(lèi)的初始化。在執(zhí)行類(lèi)的初始化期間,JVM會(huì)去獲取一個(gè)鎖。這個(gè)鎖可以同步多個(gè)線(xiàn)程對(duì)同一個(gè)類(lèi)的初始化。

基于這個(gè)特性,可以實(shí)現(xiàn)另一種線(xiàn)程安全的延遲初始化方案(這個(gè)方案被稱(chēng)之為Initialization On Demand Holder idiom):

public class InstanceFactory {
    private static class InstanceHolder {
        public static Instance instance = new Instance();
    }

    public static Instance getInstance() {
        return InstanceHolder.instance ;  //這里將導(dǎo)致InstanceHolder類(lèi)被初始化
    }
}

假設(shè)兩個(gè)線(xiàn)程并發(fā)執(zhí)行g(shù)etInstance(),下面是執(zhí)行的示意圖:

這個(gè)方案的實(shí)質(zhì)是:允許“問(wèn)題的根源”的三行偽代碼中的2和3重排序,但不允許非構(gòu)造線(xiàn)程(這里指線(xiàn)程B)“看到”這個(gè)重排序。

初始化一個(gè)類(lèi),包括執(zhí)行這個(gè)類(lèi)的靜態(tài)初始化和初始化在這個(gè)類(lèi)中聲明的靜態(tài)字段。根據(jù)java語(yǔ)言規(guī)范,在首次發(fā)生下列任意一種情況時(shí),一個(gè)類(lèi)或接口類(lèi)型T將被立即初始化:

T是一個(gè)類(lèi),而且一個(gè)T類(lèi)型的實(shí)例被創(chuàng)建;

T是一個(gè)類(lèi),且T中聲明的一個(gè)靜態(tài)方法被調(diào)用;

T中聲明的一個(gè)靜態(tài)字段被賦值;

T中聲明的一個(gè)靜態(tài)字段被使用,而且這個(gè)字段不是一個(gè)常量字段;

T是一個(gè)頂級(jí)類(lèi)(top level class,見(jiàn)java語(yǔ)言規(guī)范的§7.6),而且一個(gè)斷言語(yǔ)句嵌套在T內(nèi)部被執(zhí)行。

在InstanceFactory示例代碼中,首次執(zhí)行g(shù)etInstance()的線(xiàn)程將導(dǎo)致InstanceHolder類(lèi)被初始化(符合情況4)。

由于java語(yǔ)言是多線(xiàn)程的,多個(gè)線(xiàn)程可能在同一時(shí)間嘗試去初始化同一個(gè)類(lèi)或接口(比如這里多個(gè)線(xiàn)程可能在同一時(shí)刻調(diào)用getInstance()來(lái)初始化InstanceHolder類(lèi))。因此在java中初始化一個(gè)類(lèi)或者接口時(shí),需要做細(xì)致的同步處理。

Java語(yǔ)言規(guī)范規(guī)定,對(duì)于每一個(gè)類(lèi)或接口C,都有一個(gè)唯一的初始化鎖LC與之對(duì)應(yīng)。從C到LC的映射,由JVM的具體實(shí)現(xiàn)去自由實(shí)現(xiàn)。JVM在類(lèi)初始化期間會(huì)獲取這個(gè)初始化鎖,并且每個(gè)線(xiàn)程至少獲取一次鎖來(lái)確保這個(gè)類(lèi)已經(jīng)被初始化過(guò)了(事實(shí)上,java語(yǔ)言規(guī)范允許JVM的具體實(shí)現(xiàn)在這里做一些優(yōu)化,見(jiàn)后文的說(shuō)明)。

對(duì)于類(lèi)或接口的初始化,java語(yǔ)言規(guī)范制定了精巧而復(fù)雜的類(lèi)初始化處理過(guò)程。java初始化一個(gè)類(lèi)或接口的處理過(guò)程如下(這里對(duì)類(lèi)初始化處理過(guò)程的說(shuō)明,省略了與本文無(wú)關(guān)的部分;同時(shí)為了更好的說(shuō)明類(lèi)初始化過(guò)程中的同步處理機(jī)制,筆者人為的把類(lèi)初始化的處理過(guò)程分為了五個(gè)階段):

第一階段:通過(guò)在Class對(duì)象上同步(即獲取Class對(duì)象的初始化鎖),來(lái)控制類(lèi)或接口的初始化。這個(gè)獲取鎖的線(xiàn)程會(huì)一直等待,直到當(dāng)前線(xiàn)程能夠獲取到這個(gè)初始化鎖。

假設(shè)Class對(duì)象當(dāng)前還沒(méi)有被初始化(初始化狀態(tài)state此時(shí)被標(biāo)記為state = noInitialization),且有兩個(gè)線(xiàn)程A和B試圖同時(shí)初始化這個(gè)Class對(duì)象。下面是對(duì)應(yīng)的示意圖:

下面是這個(gè)示意圖的說(shuō)明:

|時(shí)間| 線(xiàn)程A| 線(xiàn)程B|
|t1| A1:嘗試獲取Class對(duì)象的初始化鎖。這里假設(shè)線(xiàn)程A獲取到了初始化鎖| B1:嘗試獲取Class對(duì)象的初始化鎖,由于線(xiàn)程A獲取到了鎖,線(xiàn)程B將一直等待獲取初始化鎖|
|t2| A2:線(xiàn)程A看到線(xiàn)程還未被初始化(因?yàn)樽x取到state == noInitialization),線(xiàn)程設(shè)置state = initializing||
|t3| A3:線(xiàn)程A釋放初始化鎖||

第二階段:線(xiàn)程A執(zhí)行類(lèi)的初始化,同時(shí)線(xiàn)程B在初始化鎖對(duì)應(yīng)的condition上等待:

下面是這個(gè)示意圖的說(shuō)明:

|時(shí)間| 線(xiàn)程A| 線(xiàn)程B|
|t1| A1:執(zhí)行類(lèi)的靜態(tài)初始化和初始化類(lèi)中聲明的靜態(tài)字段| B1:獲取到初始化鎖|
|t2|| B2:讀取到state == initializing|
|t3|| B3:釋放初始化鎖|
|t4|| B4:在初始化鎖的condition中等待|

第三階段:線(xiàn)程A設(shè)置state = initialized,然后喚醒在condition中等待的所有線(xiàn)程:

下面是這個(gè)示意圖的說(shuō)明:

|時(shí)間| 線(xiàn)程A|
|t1| A1:獲取初始化鎖|
|t2| A2:設(shè)置state = initialized|
|t3| A3:?jiǎn)拘言赾ondition中等待的所有線(xiàn)程|
|t4| A4:釋放初始化鎖|
|t5| A5:線(xiàn)程A的初始化處理過(guò)程完成|

第四階段:線(xiàn)程B結(jié)束類(lèi)的初始化處理:

下面是這個(gè)示意圖的說(shuō)明:

|時(shí)間| 線(xiàn)程B|
|t1| B1:獲取初始化鎖|
|t2| B2:讀取到state == initialized|
|t3| B3:釋放初始化鎖|
|t4| B4:線(xiàn)程B的類(lèi)初始化處理過(guò)程完成|

線(xiàn)程A在第二階段的A1執(zhí)行類(lèi)的初始化,并在第三階段的A4釋放初始化鎖;線(xiàn)程B在第四階段的B1獲取同一個(gè)初始化鎖,并在第四階段的B4之后才開(kāi)始訪(fǎng)問(wèn)這個(gè)類(lèi)。根據(jù)java內(nèi)存模型規(guī)范的鎖規(guī)則,這里將存在如下的happens-before關(guān)系:

這個(gè)happens-before關(guān)系將保證:線(xiàn)程A執(zhí)行類(lèi)的初始化時(shí)的寫(xiě)入操作(執(zhí)行類(lèi)的靜態(tài)初始化和初始化類(lèi)中聲明的靜態(tài)字段),線(xiàn)程B一定能看到。

第五階段:線(xiàn)程C執(zhí)行類(lèi)的初始化的處理:

下面是這個(gè)示意圖的說(shuō)明:

|時(shí)間| 線(xiàn)程C|
|t1| C1:獲取初始化鎖|
|t2| C2:讀取到state == initialized|
|t3| C3:釋放初始化鎖|
|t4| C4:線(xiàn)程C的類(lèi)初始化處理過(guò)程完成|

在第三階段之后,類(lèi)已經(jīng)完成了初始化。因此線(xiàn)程C在第五階段的類(lèi)初始化處理過(guò)程相對(duì)簡(jiǎn)單一些(前面的線(xiàn)程A和B的類(lèi)初始化處理過(guò)程都經(jīng)歷了兩次鎖獲取-鎖釋放,而線(xiàn)程C的類(lèi)初始化處理只需要經(jīng)歷一次鎖獲取-鎖釋放)。

線(xiàn)程A在第二階段的A1執(zhí)行類(lèi)的初始化,并在第三階段的A4釋放鎖;線(xiàn)程C在第五階段的C1獲取同一個(gè)鎖,并在在第五階段的C4之后才開(kāi)始訪(fǎng)問(wèn)這個(gè)類(lèi)。根據(jù)java內(nèi)存模型規(guī)范的鎖規(guī)則,這里將存在如下的happens-before關(guān)系:

這個(gè)happens-before關(guān)系將保證:線(xiàn)程A執(zhí)行類(lèi)的初始化時(shí)的寫(xiě)入操作,線(xiàn)程C一定能看到。

※注1:這里的condition和state標(biāo)記是本文虛構(gòu)出來(lái)的。Java語(yǔ)言規(guī)范并沒(méi)有硬性規(guī)定一定要使用condition和state標(biāo)記。JVM的具體實(shí)現(xiàn)只要實(shí)現(xiàn)類(lèi)似功能即可。

※注2:Java語(yǔ)言規(guī)范允許Java的具體實(shí)現(xiàn),優(yōu)化類(lèi)的初始化處理過(guò)程(對(duì)這里的第五階段做優(yōu)化),具體細(xì)節(jié)參見(jiàn)java語(yǔ)言規(guī)范的12.4.2章。

通過(guò)對(duì)比基于volatile的雙重檢查鎖定的方案和基于類(lèi)初始化的方案,我們會(huì)發(fā)現(xiàn)基于類(lèi)初始化的方案的實(shí)現(xiàn)代碼更簡(jiǎn)潔。但基于volatile的雙重檢查鎖定的方案有一個(gè)額外的優(yōu)勢(shì):除了可以對(duì)靜態(tài)字段實(shí)現(xiàn)延遲初始化外,還可以對(duì)實(shí)例字段實(shí)現(xiàn)延遲初始化。

總結(jié)

延遲初始化降低了初始化類(lèi)或創(chuàng)建實(shí)例的開(kāi)銷(xiāo),但增加了訪(fǎng)問(wèn)被延遲初始化的字段的開(kāi)銷(xiāo)。在大多數(shù)時(shí)候,正常的初始化要優(yōu)于延遲初始化。如果確實(shí)需要對(duì)實(shí)例字段使用線(xiàn)程安全的延遲初始化,請(qǐng)使用上面介紹的基于volatile的延遲初始化的方案;如果確實(shí)需要對(duì)靜態(tài)字段使用線(xiàn)程安全的延遲初始化,請(qǐng)使用上面介紹的基于類(lèi)初始化的方案。

參考文獻(xiàn)

Double-checked locking and the Singleton pattern

The Java Language Specification, Java SE 7 Edition

JSR-133: Java Memory Model and Thread Specification

Java Concurrency in Practice

Effective Java (2nd Edition)

JSR 133 (Java Memory Model) FAQ

The JSR-133 Cookbook for Compiler Writers

Java theory and practice: Fixing the Java Memory Model, Part 2

感謝方騰飛對(duì)本文的審校。

by 程曉明 via ifeve

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/64056.html

相關(guān)文章

  • 為什么雙重檢查鎖模式需要 volatile ?

    摘要:注意,禁止指令重排序在之后才被修復(fù)使用局部變量?jī)?yōu)化性能重新查看中雙重檢查鎖定代碼。幫助文檔雙重檢查鎖定與延遲初始化有關(guān)雙重檢查鎖定失效的說(shuō)明 雙重檢查鎖定(Double check locked)模式經(jīng)常會(huì)出現(xiàn)在一些框架源碼中,目的是為了延遲初始化變量。這個(gè)模式還可以用來(lái)創(chuàng)建單例。下面來(lái)看一個(gè) Spring 中雙重檢查鎖定的例子。 showImg(https://segmentfaul...

    geekzhou 評(píng)論0 收藏0
  • 淺談雙重檢查鎖定延遲始化

    摘要:非線(xiàn)程安全的雙重檢查鎖這里看起來(lái)很完美,但是是一個(gè)錯(cuò)誤的優(yōu)化,代碼在讀取到不為的時(shí)候,引用的對(duì)象有可能換沒(méi)有完成初始化,這樣返回的是有問(wèn)題的。 在Java多線(xiàn)程程序中,有時(shí)需要采用延遲初始化來(lái)降低初始化類(lèi)和創(chuàng)建對(duì)象的開(kāi)銷(xiāo),雙重檢查鎖定是常見(jiàn)的延遲初始化技術(shù),但它是一種錯(cuò)誤的用法 雙重檢查鎖的演進(jìn)以及問(wèn)題 使用syncronized實(shí)現(xiàn) public synchronized stati...

    Shonim 評(píng)論0 收藏0
  • 單例模式總結(jié)

    摘要:如果是后者,則在執(zhí)行完畢未執(zhí)行之前,被線(xiàn)程二搶占了,這時(shí)已經(jīng)是非了但卻沒(méi)有初始化,所以線(xiàn)程二會(huì)直接返回在之后雙重檢查鎖定才能夠正常達(dá)到單例效果,之前有個(gè)坑。所以,在版本前,雙重檢查鎖形式的單例模式是無(wú)法保證線(xiàn)程安全的。 第一種(懶漢, 線(xiàn)程不安全): public class Singleton { private static Singleton instance; ...

    xorpay 評(píng)論0 收藏0
  • 雙重檢查鎖定失效分析

    摘要:雙重檢查鎖定以下稱(chēng)為已被廣泛當(dāng)做多線(xiàn)程環(huán)境下延遲初始化的一種高效手段。由于沒(méi)有對(duì)這些做出明確規(guī)定,很難說(shuō)是否有效??梢栽谥惺褂蔑@式的內(nèi)存屏障來(lái)使生效,但中并沒(méi)有這些屏障。如果改變鎖釋放的語(yǔ)義釋放時(shí)執(zhí)行一個(gè)雙向的內(nèi)存屏障將會(huì)帶來(lái)性能損失。 雙重檢查鎖定(以下稱(chēng)為DCL)已被廣泛當(dāng)做多線(xiàn)程環(huán)境下延遲初始化的一種高效手段。 showImg(http://segmentfault.com/i...

    keke 評(píng)論0 收藏0
  • 單例模式雙重檢查鎖定(double-checked locking)

    摘要:對(duì)于而言,它執(zhí)行的是一個(gè)個(gè)指令。在指令中創(chuàng)建對(duì)象和賦值操作是分開(kāi)進(jìn)行的,也就是說(shuō)語(yǔ)句是分兩步執(zhí)行的。此時(shí)線(xiàn)程打算使用實(shí)例,卻發(fā)現(xiàn)它沒(méi)有被初始化,于是錯(cuò)誤發(fā)生了。 1.餓漢式單例 public class Singleton { private static Singleton instance = new Singleton(); ...

    yearsj 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<