小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Java多線程編程從一個(gè)錯(cuò)誤的雙重校驗(yàn)鎖代碼談一下volatile關(guān)鍵字

 漢無(wú)為 2018-08-26

在Java多線程編程-(2)中提及到了一段使用Synchronized關(guān)鍵字實(shí)現(xiàn)的單利模式--雙重校驗(yàn)鎖,代碼如下:

Java多線程編程從一個(gè)錯(cuò)誤的雙重校驗(yàn)鎖代碼談一下volatile關(guān)鍵字

慧眼的小伙伴,已經(jīng)發(fā)現(xiàn)了其中的問(wèn)題,并給了及時(shí)的回復(fù):

Java多線程編程從一個(gè)錯(cuò)誤的雙重校驗(yàn)鎖代碼談一下volatile關(guān)鍵字

這也是我今天準(zhǔn)備和大家一起學(xué)習(xí)的內(nèi)容。上述的代碼是錯(cuò)誤的寫法,之所以是錯(cuò)誤的,這是因?yàn)椋?strong>指令重排優(yōu)化,可能會(huì)導(dǎo)致初始化單利對(duì)象和將該對(duì)象地址賦值給instance字段的順序與上面Java代碼中書寫的順序不同。

例如:線程A在創(chuàng)建單例對(duì)象時(shí),在構(gòu)造方法被調(diào)用之前,就為該對(duì)象分配了內(nèi)存空間并將對(duì)象設(shè)置為默認(rèn)值。此時(shí)線程A就可以將分配的內(nèi)存地址賦值給instance字段了,然而該對(duì)象可能還沒(méi)有完成初始化操作。線程B來(lái)調(diào)用newInstance()方法,得到的就是為初始化完全的單例對(duì)象,這就會(huì)導(dǎo)致系統(tǒng)出現(xiàn)異常行為。

為了解決上述的問(wèn)題,可以使用volatile關(guān)鍵字進(jìn)行修飾instance字段。volatile關(guān)鍵字在這里的含義就是禁止指令的重排序優(yōu)化(另一個(gè)作用是提供內(nèi)存可見性),從而保證instance字段被初始化時(shí),單例對(duì)象已經(jīng)被完全初始化。

最終代碼如下:

Java多線程編程從一個(gè)錯(cuò)誤的雙重校驗(yàn)鎖代碼談一下volatile關(guān)鍵字

那么問(wèn)題來(lái)了,為什么volatile關(guān)鍵字可以實(shí)現(xiàn)禁止指令的重排序優(yōu)化以及什么是指令重排序優(yōu)化哪?

在Java內(nèi)存模型中我們都是圍繞著原子性、有序性和可見性進(jìn)行討論的。為了確保線程間的原子性、有序性和可見性,Java中使用了一些特殊的關(guān)鍵字申明或者是特殊的操作來(lái)告訴虛擬機(jī),在這個(gè)地方,要注意一下,不能隨意變動(dòng)優(yōu)化目標(biāo)指令。關(guān)鍵字volatile就是其中之一。

指令重排序是JVM為了優(yōu)化指令,提高程序運(yùn)行效率,在不影響單線程程序執(zhí)行結(jié)果的前提下,盡可能地提高并行度(比如:將多條指定并行執(zhí)行或者是調(diào)整指令的執(zhí)行順序)。編譯器、處理器也遵循這樣一個(gè)目標(biāo)。注意是單線程。可顯而知,多線程的情況下指令重排序就會(huì)給程序員帶來(lái)問(wèn)題。

最重要的一個(gè)問(wèn)題就是程序執(zhí)行的順序可能會(huì)被調(diào)整,另一個(gè)問(wèn)題是對(duì)修改的屬性無(wú)法及時(shí)的通知其他線程,已達(dá)到所有線程操作該屬性的可見性。

根據(jù)編譯器的優(yōu)化規(guī)則,如果不使用volatile關(guān)鍵字對(duì)變量進(jìn)行修飾的,那么這個(gè)變量被修改后,其他線程可能并不會(huì)被通知到,甚至在別的想愛你城中,看到變量修改順序都會(huì)是反的。一旦使用volatile關(guān)鍵字進(jìn)行修飾的話,虛擬機(jī)就會(huì)特別小心的處理這種情況。

因此,如何正確的使用雙重校驗(yàn)鎖,以及為什么使用關(guān)鍵字volatile這里我們應(yīng)該很清楚了。

上述也提到了volatile關(guān)鍵字的另一個(gè)作用就是:變量在多個(gè)線程之間可見。

volatile可見性

首先我們先看一下段代碼:

Java多線程編程從一個(gè)錯(cuò)誤的雙重校驗(yàn)鎖代碼談一下volatile關(guān)鍵字

執(zhí)行結(jié)果:

Java多線程編程從一個(gè)錯(cuò)誤的雙重校驗(yàn)鎖代碼談一下volatile關(guān)鍵字

可以看出 在單線程的情況下,程序會(huì)一直執(zhí)行下去,即一直執(zhí)行while循環(huán),導(dǎo)致程序不能正常執(zhí)行下邊的代碼。解決的方法可以使用多線程。多線程示例代碼如下:

Java多線程編程從一個(gè)錯(cuò)誤的雙重校驗(yàn)鎖代碼談一下volatile關(guān)鍵字

執(zhí)行結(jié)果如下:

Java多線程編程從一個(gè)錯(cuò)誤的雙重校驗(yàn)鎖代碼談一下volatile關(guān)鍵字

可以看出使用多線程的技術(shù)實(shí)現(xiàn),但是有一個(gè)問(wèn)題就是在一些平臺(tái)上執(zhí)行的時(shí)候會(huì)出現(xiàn)死鎖的情況,解決的方法就是使用volatile關(guān)鍵字。即變量用volatile關(guān)鍵字修飾。

volatile關(guān)鍵字的作用就是強(qiáng)制從公共堆棧中取得變量的值,而不是線程私有的數(shù)據(jù)棧中取得變量的值。

Java多線程編程從一個(gè)錯(cuò)誤的雙重校驗(yàn)鎖代碼談一下volatile關(guān)鍵字

volatile與synchronized的區(qū)別

1、關(guān)鍵字volatile是線程同步的輕量級(jí)實(shí)現(xiàn),性能比synchronized要好,并且volatile只能修于變量,而synchronized可以修飾方法,代碼塊等。

2、多線程訪問(wèn)volatile不會(huì)發(fā)生阻塞,而synchronized會(huì)發(fā)生阻塞。

3、volatile可以保證數(shù)據(jù)的可見性,但不可以保證原子性,而synchronized可以保證原子性,也可以間接保證可見性,因?yàn)樗麜?huì)將私有內(nèi)存和公共內(nèi)存中的數(shù)據(jù)做同步。

4、volatile解決 的是變量在多個(gè)線程之間的可見性,而synchronized解決的是多個(gè)線程之間訪問(wèn)資源的同步性。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多