日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java dcl 失效解决_DCL失效原因和解决方案

發(fā)布時(shí)間:2023/12/10 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java dcl 失效解决_DCL失效原因和解决方案 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Java內(nèi)存模型? ? 在了解Java的同步秘密之前,先來看看JMM(Java Memory Model)。

Java被設(shè)計(jì)為跨平臺(tái)的語言,在內(nèi)存管理上,顯然也要有一個(gè)統(tǒng)一的模型。而且Java語言最大的特點(diǎn)就是廢除了指針,把程序員從痛苦中解脫出來,不用再考慮內(nèi)存使用和管理方面的問題。

可惜世事總不盡如人意,雖然JMM設(shè)計(jì)上方便了程序員,但是它增加了虛擬機(jī)的復(fù)雜程度,而且還導(dǎo)致某些編程技巧在Java語言中失效。

JMM主要是為了規(guī)定了線程和內(nèi)存之間的一些關(guān)系。對(duì)Java程序員來說只需負(fù)責(zé)用synchronized同步關(guān)鍵字,其它諸如與線程/內(nèi)存之間進(jìn)行數(shù)據(jù)交換/同步等繁瑣工作均由虛擬機(jī)負(fù)責(zé)完成。根據(jù)JMM的設(shè)計(jì),系統(tǒng)存在一個(gè)主內(nèi)存(Main Memory),Java中所有變量都儲(chǔ)存在主存中,對(duì)于所有線程都是共享的。每條線程都有自己的工作內(nèi)存(Working Memory),工作內(nèi)存中保存的是主存中某些變量的拷貝,線程對(duì)所有變量的操作都是在工作內(nèi)存中進(jìn)行,線程之間無法相互直接訪問,變量傳遞均需要通過主存完成。

線程若要對(duì)某變量進(jìn)行操作,必須經(jīng)過一系列步驟:首先從主存復(fù)制/刷新數(shù)據(jù)到工作內(nèi)存,然后執(zhí)行代碼,進(jìn)行引用/賦值操作,最后把變量?jī)?nèi)容寫回Main Memory。Java語言規(guī)范(JLS)中對(duì)線程和主存互操作定義了6個(gè)行為,分別為load,save,read,write,assign和use,這些操作行為具有原子性,且相互依賴,有明確的調(diào)用先后順序。具體的描述請(qǐng)參見JLS第17章。

我們?cè)谇懊娴恼鹿?jié)介紹了synchronized的作用,現(xiàn)在,從JMM的角度來重新審視synchronized關(guān)鍵字。

假設(shè)某條線程執(zhí)行一個(gè)synchronized代碼段,其間對(duì)某變量進(jìn)行操作,JVM會(huì)依次執(zhí)行如下動(dòng)作:

(1) 獲取同步對(duì)象monitor (lock)

(2) 從主存復(fù)制變量到當(dāng)前工作內(nèi)存 (read and load)

(3) 執(zhí)行代碼,改變共享變量值 (use and assign)

(4) 用工作內(nèi)存數(shù)據(jù)刷新主存相關(guān)內(nèi)容 (store and write)

(5) 釋放同步對(duì)象鎖 (unlock)

可見,synchronized的另外一個(gè)作用是保證主存內(nèi)容和線程的工作內(nèi)存中的數(shù)據(jù)的一致性。如果沒有使用synchronized關(guān)鍵字,JVM不保證第2步和第4步會(huì)嚴(yán)格按照上述次序立即執(zhí)行。因?yàn)楦鶕?jù)JLS中的規(guī)定,線程的工作內(nèi)存和主存之間的數(shù)據(jù)交換是松耦合的,什么時(shí)候需要刷新工作內(nèi)存或者更新主內(nèi)存內(nèi)容,可以由具體的虛擬機(jī)實(shí)現(xiàn)自行決定。如果多個(gè)線程同時(shí)執(zhí)行一段未經(jīng)synchronized保護(hù)的代碼段,很有可能某條線程已經(jīng)改動(dòng)了變量的值,但是其他線程卻無法看到這個(gè)改動(dòng),依然在舊的變量值上進(jìn)行運(yùn)算,最終導(dǎo)致不可預(yù)料的運(yùn)算結(jié)果。

DCL失效? ?針對(duì)延遲加載法的同步實(shí)現(xiàn)所產(chǎn)生的性能低的問題,我們可以采用DCL,即雙重檢查加鎖(Double Check Lock)的方法來避免每次調(diào)用getInstance()方法時(shí)都同步。

在開始討論之前,先介紹一下LazyLoad,這種技巧很常用,就是指一個(gè)類包含某個(gè)成員變量,在類初始化的時(shí)候并不立即為該變量初始化一個(gè)實(shí)例,而是等到真正要使用到該變量的時(shí)候才初始化之。

例如下面的代碼:class Foo {

private Resource res = null;

public Resource getResource() {

if (res == null)

res = new Resource();

return res;

}

}? ?由于LazyLoad可以有效的減少系統(tǒng)資源消耗,提高程序整體的性能,所以被廣泛的使用,連Java的缺省類加載器也采用這種方法來加載Java類。

在單線程環(huán)境下,一切都相安無事,但如果把上面的代碼放到多線程環(huán)境下運(yùn)行,那么就可能會(huì)出現(xiàn)問題。假設(shè)有2條線程,同時(shí)執(zhí)行到了if(res == null),那么很有可能res被初始化2次,為了避免這樣的Race Condition,得用synchronized關(guān)鍵字把上面的方法同步起來。代碼如下:

Class Foo {

private Resource res = null;

public synchronized Resource getResource() {

if (res == null)

res = new Resource();

return res;

}

}? ?synchronized過的方法在速度上要比未同步的方法慢上100倍,同時(shí)你也發(fā)現(xiàn),只有第一次調(diào)用該方法的時(shí)候才需要同步,而一旦res初始化完成,同步完全沒必要。所以你很快就把代碼重構(gòu)成了下面的樣子:

Class Foo {

private Resource res = null;

private Date d = new Data();

public Resource getResource() {

if (res == null){ //(1)

synchronized(Foo.class){

if(res == null){

res = new Resource(); //(2)

}

}

}

return res;

}

}Double-Checked Locking看起來是非常完美的。但是很遺憾,根據(jù)Java的語言規(guī)范,上面的代碼是不可靠的。

出現(xiàn)上述問題, 最重要的2個(gè)原因如下:

1, 編譯器優(yōu)化了程序指令, 以加快cpu處理速度.

2, 多核cpu動(dòng)態(tài)調(diào)整指令順序, 以加快并行運(yùn)算能力.

問題出現(xiàn)的順序:

1, 線程A, 發(fā)現(xiàn)對(duì)象未實(shí)例化, 準(zhǔn)備開始實(shí)例化

2, 由于編譯器優(yōu)化了程序指令, 允許對(duì)象在構(gòu)造函數(shù)未調(diào)用完前, 將共享變量的引用指向部分構(gòu)造的對(duì)象, 雖然對(duì)象未完全實(shí)例化, 但已經(jīng)不為null了.

3, 線程B, 發(fā)現(xiàn)部分構(gòu)造的對(duì)象已不是null, 則直接返回了該對(duì)象.無法保證語句(2)和語句(1)不存在happen-before(詳解跳轉(zhuǎn))關(guān)系.

一個(gè)線程A運(yùn)行到"這里"時(shí),A的工作區(qū)中,肯定已經(jīng)產(chǎn)生一個(gè)Foo對(duì)象,而且這時(shí)這個(gè)對(duì)象已經(jīng)完成了Data d.現(xiàn)在線程A調(diào)用時(shí)間到,執(zhí)行權(quán)被切換到另一個(gè)線程B來執(zhí)行,會(huì)有什么問題呢?如果res不為null,線程B獲得了一個(gè)res,但可能res.getD()卻還沒有初始化.

對(duì)于"這里"這條語句,線程A還沒有離開同步塊.因?yàn)闆]有"離開同步塊"這個(gè)條件,線程A的工作區(qū)沒有強(qiáng)制與主存儲(chǔ)器同步,這時(shí)工作區(qū)中有兩個(gè)字段res,d。雖然在線程A的工作區(qū)res和d都是完整的,但有JSL沒有強(qiáng)制不允許先把res映射到主存儲(chǔ)區(qū),如果哪個(gè)jvm實(shí)現(xiàn)按它的優(yōu)化方案先把工作存儲(chǔ)器中的res同步到主存儲(chǔ)器了,這時(shí)正好線程B獲取了,而d卻沒有同步過去,那么線程B就獲取了res的引用卻找不能res.getD()。

DCL解決方案1.DCL的替代 Initialize-On-Demand

Class ResSingleton {

public static Resource res = new Resource();

}

這里L(fēng)azyFoo只有一個(gè)靜態(tài)成員變量。當(dāng)?shù)谝淮问褂肦esSingleton.res的時(shí)候,JVM才會(huì)初始化一個(gè)Resource實(shí)例,并且JVM會(huì)保證初始化的結(jié)果及時(shí)寫入主存,能讓其他線程看到,這樣就成功的實(shí)現(xiàn)了LazyLoad。

2、另外,可以將instance聲明為volatile(詳解跳轉(zhuǎn)),即

private volatile static LazySingleton instance;

在讀線程B讀一個(gè)volatile變量后,寫線程A在寫這個(gè)volatile變量之前,所有可見的共享變量的值都將立即變得對(duì)讀線程B可見。

參考:http://www.blogjava.net/weidagang2046/articles/3494.html

總結(jié)

以上是生活随笔為你收集整理的java dcl 失效解决_DCL失效原因和解决方案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。