溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何用JAVA語言分析雙重檢查鎖定

發(fā)布時間:2022-01-18 15:03:04 來源:億速云 閱讀:120 作者:柒染 欄目:網(wǎng)絡(luò)管理

這篇文章跟大家分析一下“如何用JAVA語言分析雙重檢查鎖定”。內(nèi)容詳細(xì)易懂,對“如何用JAVA語言分析雙重檢查鎖定”感興趣的朋友可以跟著小編的思路慢慢深入來閱讀一下,希望閱讀后能夠?qū)Υ蠹矣兴鶐椭?。下面跟著小編一起深入學(xué)習(xí)“如何用JAVA語言分析雙重檢查鎖定”的知識吧。

1、雙重檢查鎖定

在程序開發(fā)中,有時需要推遲一些高開銷的對象初始化操作,并且只有在使用這些對象時才進行初始化,此時可以采用雙重檢查鎖定來延遲對象初始化操作。雙重檢查鎖定是設(shè)計用來減少并發(fā)系統(tǒng)中競爭和同步開銷的一種軟件設(shè)計模式,在普通單例模式的基礎(chǔ)上,先判斷對象是否已經(jīng)被初始化,再決定要不要加鎖。盡管雙重檢查鎖定解決了普通單例模式的在多線程環(huán)境中易出錯和線程不安全的問題,但仍然存在一些隱患。下面以JAVA語言源代碼為例,分析雙重檢查鎖定缺陷產(chǎn)生的原因以及修復(fù)方法。

2、 雙重檢查鎖定的危害

雙重檢查鎖定在單線程環(huán)境中并無影響,在多線程環(huán)境下,由于線程隨時會相互切換執(zhí)行,在指令重排的情況下,對象未實例化完全,導(dǎo)致程序調(diào)用出錯。

3、示例代碼

示例源于Samate Juliet Test Suite for Java v1.3 (https://samate.nist.gov/SARD/testsuite.php),源文件名:CWE609_Double_Checked_Locking__Servlet_01.java。

3.1缺陷代碼

如何用JAVA語言分析雙重檢查鎖定

上述代碼行23行-38行,程序先判斷 stringBad 是否為 null,如果不是則直接返回該 String 對象,這樣避免了進入 synchronized 塊所需要花費的資源。當(dāng) stringBad 為 null 時,使用 synchronized 關(guān)鍵字在多線程環(huán)境中避免多次創(chuàng)建 String 對象。在代碼實際運行時,以上代碼仍然可能發(fā)生錯誤。

對于第33行,創(chuàng)建 stringBad 對象和賦值操作是分兩步執(zhí)行的。但 JVM 不保證這兩個操作的先后順序。當(dāng)指令重排序后,JVM 會先賦值指向了內(nèi)存地址,然后再初始化 stringBad 對象。如果此時存在兩個線程,兩個線程同時進入了第27行。線程1首先進入了 synchronized 塊,由于 stringBad 為 null,所以它執(zhí)行了第33行。當(dāng) JVM 對指令進行了重排序,JVM 先分配了實例的空白內(nèi)存,并賦值給 stringBad,但這時 stringBad 對象還未實例化,然后線程1離開了 synchronized 塊。當(dāng)線程2進入 synchronized 塊時,由于 stringBad 此時不是 null ,直接返回了未被實例化的對象(僅有內(nèi)存地址值,對象實際未初始化)。后續(xù)線程2調(diào)用程序?qū)?stringBad 對象進行操作時,此時的對象未被初始化,于是錯誤發(fā)生。

使用360代碼衛(wèi)士對上述示例代碼進行檢測,可以檢出“雙重檢查鎖定”缺陷,顯示等級為中。在代碼行第27行報出缺陷,如圖1所示:


如何用JAVA語言分析雙重檢查鎖定

圖1:“雙重檢查鎖定”的檢測示例

3.2 修復(fù)代碼

如何用JAVA語言分析雙重檢查鎖定

在上述修復(fù)代碼中,在第23行使用 volatile 關(guān)鍵字來對單例變量 stringBad 進行修飾。 volatile 作為指令關(guān)鍵字確保指令不會因編譯器的優(yōu)化而省略,且要求每次直接讀值。

由于編譯器優(yōu)化,代碼在實際執(zhí)行的時候可能與我們編寫的順序不同。編譯器只保證程序執(zhí)行結(jié)果與源代碼相同,卻不保證實際指令的順序與源代碼相同,在單線程環(huán)境中并不會出錯,然而一旦引入多線程環(huán)境,這種亂序就可能導(dǎo)致嚴(yán)重問題。 volatile 關(guān)鍵字就可以從語義上解決這個問題,值得關(guān)注的是 volatile 的禁止指令重排序優(yōu)化功能在 Java 1.5 后才得以實現(xiàn),因此1.5 前的版本仍然是不安全的,即使使用了 volatile 關(guān)鍵字。

使用360代碼衛(wèi)士對修復(fù)后的代碼進行檢測,可以看到已不存在“雙重檢查鎖定”缺陷。如圖2:


如何用JAVA語言分析雙重檢查鎖定

圖2:修復(fù)后檢測結(jié)果

4 、如何避免雙重檢查鎖定

要避免雙重檢查鎖定,需要注意以下幾點:

(1)使用 volatile 關(guān)鍵字避免指令重排序,但這個解決方案需要 JDK5 或更高版本,因為從JDK5 開始使用新的 JSR-133 內(nèi)存模型規(guī)范,這個規(guī)范增強了 volatile 的語義。

(2)基于類初始化的解決方案。

如何用JAVA語言分析雙重檢查鎖定JVM在類的初始化階段(即在Class被加載后,且被線程使用之前),會執(zhí)行類的初始化。在執(zhí)行類的初始化期間,JVM會去獲取一個鎖。這個鎖可以同步多個線程對同一個類的初始化。

關(guān)于如何用JAVA語言分析雙重檢查鎖定就分享到這里啦,希望上述內(nèi)容能夠讓大家有所提升。如果想要學(xué)習(xí)更多知識,請大家多多留意小編的更新。謝謝大家關(guān)注一下億速云網(wǎng)站!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI