摘要:同樣,用類型的變量來保存這些值也不是線程安全的。僅保證可見性,無法保證線程安全性。并且返回的結(jié)果是對象,是局部變量,并未使對象逸出,所以這里也是線程安全的。
《Java并發(fā)編程實戰(zhàn)》第3章原文 《Java并發(fā)編程實戰(zhàn)》中3.4.2 示例:使用Volatile類型來發(fā)布不可變對象
在前面的UnsafeCachingFactorizer類中,我們嘗試用兩個AtomicReferences變量來保存最新的數(shù)值及其因數(shù)分解結(jié)果,但這種方式并非是線程安全的,因為我們無法以原子方式來同時讀取或更新這兩個相關(guān)的值。同樣,用volatile類型的變量來保存這些值也不是線程安全的。然而,在某些情況下,不可變對象能提供一種弱形式的原子性。
因式分解Servlet將執(zhí)行兩個原子操作:更新緩存的結(jié)果,以及通過判斷緩存中的數(shù)值是否等于請求的數(shù)值來決定是否直接讀取緩存中的因數(shù)分解結(jié)果。每當(dāng)需要對一組相關(guān)數(shù)據(jù)以原子方式執(zhí)行某個操作時,就可以考慮創(chuàng)建一個不可變的類來包含這些數(shù)據(jù),例如程序清單3-12中的OneValueCache。
@Immutable class OneValueCache { private final BigInteger lastNumber; private final BigInteger[] lastFactors; public OneValueCache(BigInteger i, BigInteger[] factors) { lastNumber = i; lastFactors = Arrays.copyOf(factors, factors.length); } public BigInteger[] getFactors(BigInteger i) { if (lastNumber == null || !lastNumber.equals(i)) return null; else return Arrays.copyOf(lastFactors, lastFactors.length); } }
對于在訪問和更新多個相關(guān)變量時出現(xiàn)的競爭條件問題,可以通過將這些變量全部保存在一個不可變對象中來消除。如果是一個可變的對象,那么就必須使用鎖來確保原子性。如果是一個不可變對象,那么當(dāng)線程獲得了對該對象的引用后,就不必?fù)?dān)心另一個線程會修改對象的狀態(tài)。如果要更新這些變量,那么可以創(chuàng)建一個新的容器對象,但其他使用原有對象的線程仍然會看到對象處于一致的狀態(tài)。
程序清單3-13中的VolatileCachedFactorizer使用了OneValueCache來保存緩存的數(shù)值及其因數(shù)。當(dāng)一個線程將volatile類型的cache設(shè)置為引用一個新的OneValueCache時,其他線程就會立即看到新緩存的數(shù)據(jù)。
@ThreadSafe public class VolatileCachedFactorizer implements Servlet { private volatile OneValueCache cache = new OneValueCache(null, null); public void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req); BigInteger[] factors = cache.getFactors(i); if (factors == null) { factorfactors = factor(i); cache = new OneValueCache(i, factors); } encodeIntoResponse(resp, factors); } }
與cache相關(guān)的操作不會相互干擾,因為OneValueCache是不可變的,并且在每條相應(yīng)的代碼路徑中只會訪問它一次。
通過使用包含多個狀態(tài)變量的容器對象來維持不變性條件,并使用一個volatile類型的引用來確??梢娦裕沟肰olatileCachedFactorizer在沒有顯式地使用鎖的情況下仍然是線程安全的。
程序清單3-13中存在『先檢查后執(zhí)行』(Check-Then-Act)的競態(tài)條件。
OneValueCache類的不可變性僅保證了對象的原子性。
volatile僅保證可見性,無法保證線程安全性。
綜上,對象的不可變性+volatile可見性,并不能解決競態(tài)條件的并發(fā)問題,所以原文的這段結(jié)論是錯誤的。
更新疑惑已經(jīng)解決了。
結(jié)論:
cache對象在service()中只有一處寫操作(創(chuàng)建新的cache對象),其余都是讀操作,這里符合volatile的應(yīng)用場景,確保cache對象對其他線程的可見性,不會出現(xiàn)并發(fā)讀的問題。并且返回的結(jié)果是factors對象,factors是局部變量,并未使cache對象逸出,所以這里也是線程安全的。
該問題已經(jīng)提交到提問區(qū)和知乎:
https://segmentfault.com/q/10...
https://www.zhihu.com/questio...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/66397.html
摘要:是需要我們?nèi)ヌ幚砗芏嗍虑椋瑸榱朔乐苟嗑€程給我們帶來的安全和性能的問題下面就來簡單總結(jié)一下我們需要哪些知識點來解決多線程遇到的問題。 前言 不小心就鴿了幾天沒有更新了,這個星期回家咯。在學(xué)校的日子要努力一點才行! 只有光頭才能變強 回顧前面: 多線程三分鐘就可以入個門了! Thread源碼剖析 本文章的知識主要參考《Java并發(fā)編程實戰(zhàn)》這本書的前4章,這本書的前4章都是講解并發(fā)的基...
摘要:當(dāng)某個不應(yīng)該發(fā)布的對象被發(fā)布時,這種情況被稱為逸出。線程安全共享線程安全的對象在其內(nèi)部實現(xiàn)同步,因此多線程可以通過對象的公有接口來進(jìn)行訪問而不需要進(jìn)一步的同步。 前言 本系列博客是對《Java并發(fā)編程實戰(zhàn)》的一點總結(jié),本篇主要講解以下幾個內(nèi)容,內(nèi)容會比較枯燥??赡艽蠹铱礃?biāo)題不能能直觀的感受出到底什么意思,這就是專業(yè)術(shù)語,哈哈,解釋下,術(shù)語(terminology)是在特定學(xué)科領(lǐng)域用...
摘要:發(fā)布的對象內(nèi)部狀態(tài)可能會破壞封裝性,使程序難以維持不變性條件。不變性線程安全性是不可變對象的固有屬性之一??勺儗ο蟊仨毻ㄟ^安全方式來發(fā)布,并且必須是線程安全的或者有某個鎖保護(hù)起來。 線程的優(yōu)缺點 線程是系統(tǒng)調(diào)度的基本單位。線程如果使用得當(dāng),可以有效地降低程序的開發(fā)和維護(hù)等成本,同時提升復(fù)雜應(yīng)用程序的性能。多線程程序可以通過提高處理器資源的利用率來提升系統(tǒng)的吞吐率。與此同時,在線程的使用...
摘要:多線程環(huán)境下的一些問題安全性問題在沒有正確同步的情況下,多線程環(huán)境下程序可能得出錯誤的結(jié)果。一些相關(guān)概念競爭條件多線程的環(huán)境下,程序執(zhí)行的結(jié)果取決于線程交替執(zhí)行的方式。而線程的交替操作順序是不可預(yù)測的,如此程序執(zhí)行的結(jié)果也是不可預(yù)測的。 入口 Java多線程的應(yīng)用復(fù)雜性之如jvm有限的幾個內(nèi)存方面的操作和規(guī)范,就像無數(shù)紛繁復(fù)雜的應(yīng)用邏輯建立在有限的指令集上。 如何寫出線程安全的程序,有...
摘要:共享內(nèi)存相信對并發(fā)有所了解的同學(xué)都應(yīng)該知道在推出后,對內(nèi)存管理有了更高標(biāo)準(zhǔn)的規(guī)范了,這使我們開發(fā)并發(fā)程序也有更好的標(biāo)準(zhǔn)了,不會有一些模糊的定義導(dǎo)致的無法確定的錯誤。 通過前幾篇的學(xué)習(xí),相信大家對Akka應(yīng)該有所了解了,都說解決并發(fā)哪家強,JVM上面找Akka,那么Akka到底在解決并發(fā)問題上幫我們做了什么呢? 共享內(nèi)存 眾所周知,在處理并發(fā)問題上面,最核心的一部分就是如何處理共享內(nèi)存,...
閱讀 5353·2021-09-22 15:50
閱讀 1905·2021-09-02 15:15
閱讀 1193·2019-08-29 12:49
閱讀 2570·2019-08-26 13:31
閱讀 3488·2019-08-26 12:09
閱讀 1239·2019-08-23 18:17
閱讀 2765·2019-08-23 17:56
閱讀 2959·2019-08-23 16:02