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