日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

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

 漢無為 2018-08-26

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

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

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

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

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

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

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

最終代碼如下:

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

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

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

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

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

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

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

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

volatile可見性

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

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

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

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

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

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

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

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

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

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

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

volatile與synchronized的區(qū)別

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

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

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

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

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

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多