国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

第2章-線程安全性

2019-11-11 05:19:07
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

在構(gòu)建穩(wěn)健的并發(fā)程序時(shí),必須正確地使用線程和鎖,但這些終歸是一些機(jī)制。要編寫(xiě)線程安全的代碼,其核心在于要對(duì)狀態(tài)訪問(wèn)操作進(jìn)行管理,特別是對(duì)共享的(Shared)和可變的(Mutable)狀態(tài)的訪問(wèn)。

從非正式的意義上說(shuō),對(duì)象的狀態(tài)是指存儲(chǔ)在狀態(tài)變量(例如實(shí)例或靜態(tài)域)中的數(shù)據(jù)。對(duì)象的狀態(tài)可能包括其他依賴對(duì)象的域,比如某個(gè)HashMap的狀態(tài)不僅存儲(chǔ)在HashMap對(duì)象本身,還存儲(chǔ)在許多Map.Entry對(duì)象中。

共享 意味著變量可以由多個(gè)線程同時(shí)訪問(wèn) 可變 意味著變量的值在其生命周期內(nèi)可以發(fā)生變化

一個(gè)對(duì)象是否需要是線程安全的,取決于它是否被多個(gè)線程訪問(wèn)。

當(dāng)多個(gè)線程訪問(wèn)某個(gè)狀態(tài)變量并且其中有一個(gè)線程執(zhí)行寫(xiě)入操作(說(shuō)明是是共享 可變的)時(shí),必須采用同步機(jī)制來(lái)協(xié)同這些線程對(duì)變量的訪問(wèn)。(保證線程安全性)

java中主要的同步機(jī)制:

synchronizedvolatile類型的變量顯式鎖(Explicit Lock)原子變量

變量為線程安全的方法組合:

不共享共享+不可變共享+可變+同步

程序狀態(tài)的封裝性越好,就越容易實(shí)現(xiàn)程序的線程安全性,并且代碼的維護(hù)人員也越容易保持這種方式。

線程安全的程序不一定完全由線程安全類構(gòu)成。(可以有非線程安全類,然后在程序中增加同步措施) 完全由線程安全類構(gòu)成的程序并不一定就是線程安全的。(兩個(gè)線程安全類不同鎖,構(gòu)成的程序不能保證原子性) 線程安全類中也可以包含非線程安全的類(同上,只要再增加同步措施即可)

線程安全性定義:當(dāng)多個(gè)線程訪問(wèn)某個(gè)類時(shí),不管運(yùn)行時(shí)環(huán)境采用何種調(diào)度方式或者這些線程如何交替執(zhí)行,并且在主調(diào)代碼中不需要任何額外的同步或協(xié)同,這個(gè)類始終都能表現(xiàn)出正確的行為,那么就稱這個(gè)類是線程安全的。正確性的含義是,某個(gè)類的行為與其規(guī)范一致。(如果你覺(jué)得‘正確性’的定義有些模糊,那么可以將線程安全類認(rèn)為是一個(gè)在并發(fā)環(huán)境和單線程環(huán)境中都不會(huì)被破壞的類)

通常,線程安全性的需求并非來(lái)源于對(duì)線程的直接使用,而是使用像Servlet這樣的框架。

無(wú)狀態(tài)對(duì)象一定是線程安全的。

競(jìng)態(tài)條件(Race Condition):由于不恰當(dāng)?shù)膱?zhí)行時(shí)序而出現(xiàn)了不正確的結(jié)果(出現(xiàn)這種狀況則不是線程安全的,因?yàn)檫`反了線程安全性的定義)。當(dāng)某個(gè)計(jì)算的正確性取決于多個(gè)線程的交替執(zhí)行時(shí)序時(shí),那么就會(huì)發(fā)生競(jìng)態(tài)條件,換句話說(shuō),就是正確的結(jié)果取決于運(yùn)氣。

數(shù)據(jù)競(jìng)爭(zhēng)(Data Race):如果在訪問(wèn)共享的非final類型的域(共享 可變)時(shí)沒(méi)有采用同步來(lái)進(jìn)行協(xié)同,那么就會(huì)出現(xiàn)數(shù)據(jù)競(jìng)爭(zhēng)。在java內(nèi)存模型中,如果在代碼中存在數(shù)據(jù)競(jìng)爭(zhēng),那么這段代碼就沒(méi)有確定的語(yǔ)義。

并非所有的競(jìng)態(tài)條件都是數(shù)據(jù)競(jìng)爭(zhēng),同樣并非所有的數(shù)據(jù)競(jìng)爭(zhēng)都是競(jìng)態(tài)條件。???

競(jìng)態(tài)條件的類型:

讀取-修改-寫(xiě)入(++count 操作并非原子,結(jié)果狀態(tài)依賴于之前的狀態(tài))先檢查后執(zhí)行(Check-Then-Act,通過(guò)一個(gè)可能失效的觀測(cè)結(jié)果來(lái)做出判斷或者執(zhí)行某個(gè)計(jì)算)

使用“先檢查后執(zhí)行”的一種常見(jiàn)情況就是延遲初始化。延遲初始化的母的:

將對(duì)象的初始化操作推遲到實(shí)際被使用時(shí)才進(jìn)行確保只被初始化一次。

與大多數(shù)并發(fā)錯(cuò)誤一樣,競(jìng)態(tài)條件并不總是產(chǎn)生錯(cuò)誤,還需要某種不恰當(dāng)?shù)膱?zhí)行時(shí)序。

要避免競(jìng)態(tài)條件問(wèn)題,就必須在某個(gè)線程修改該變量時(shí),通過(guò)某種方式防止其他線程使用這個(gè)變量。也就是說(shuō)必須原子操作來(lái)避免產(chǎn)生競(jìng)態(tài)條件。

原子操作:對(duì)于訪問(wèn)同一個(gè)狀態(tài)的所有操作(包括該操作本身)來(lái)說(shuō),這個(gè)操作 一個(gè) 以原子方式執(zhí)行的操作。

如果++count是一個(gè)原子操作,那么競(jìng)態(tài)條件就不會(huì)發(fā)生。 使++count不會(huì)發(fā)生競(jìng)態(tài)條件的方法

加鎖,確保原子性使用線程安全類,將count聲明為AtomicLong類型

當(dāng)在無(wú)狀態(tài)的類中添加一個(gè)狀態(tài)時(shí),如果該狀態(tài)完全由線程安全的對(duì)象來(lái)管理,那么這個(gè)類仍然是線程安全的。(0 –> 1 當(dāng)0/1變多時(shí),并不是這么簡(jiǎn)單)

當(dāng)一個(gè)類引入了多個(gè)狀態(tài)變量時(shí),狀態(tài)變量之間可能不是彼此獨(dú)立的,而是某個(gè)變量的值會(huì)對(duì)其他變量的值產(chǎn)生約束,這時(shí),要保持狀態(tài)的一致性(也就是保證線程安全),就需要在單個(gè)原子操作中更新所有相關(guān)的狀態(tài)變量。

同步機(jī)制的兩個(gè)重要方面:

原子性可見(jiàn)性

同步代碼塊包含兩部分:

作為鎖的對(duì)象引用作為由這個(gè)鎖保護(hù)的代碼塊

每個(gè)java對(duì)象都可以用作一個(gè)實(shí)現(xiàn)同步的鎖,這些鎖被稱為內(nèi)置鎖(Intrinsic Lock)或者監(jiān)視器鎖(Monitor Lock)。線程在進(jìn)入同步代碼塊之前會(huì)自動(dòng)獲得鎖,并且在退出同步代碼塊時(shí)自動(dòng)釋放鎖,而無(wú)論是通過(guò)正常的控制路徑退出,還是通過(guò)從代碼塊中拋出異常退出獲得內(nèi)置鎖的唯一途徑就是進(jìn)入由這個(gè)鎖保護(hù)的同步代碼塊或方法。

java的內(nèi)置鎖相當(dāng)于一種互斥體(或互斥鎖 mutex),這意味著最多只有一個(gè)線程能夠持有這種鎖。

并發(fā)環(huán)境中的原子性與實(shí)務(wù)應(yīng)用程序中的原子性有著相同的含義———一組語(yǔ)句作為一個(gè)不可分割的單元被執(zhí)行。

內(nèi)置鎖是可重入的(reentrant),也就是說(shuō)如果某個(gè)線程試圖獲得一個(gè)已經(jīng)由他自己持有的鎖,那么這個(gè)請(qǐng)求就會(huì)成功。“重入”意味著獲取鎖的操作粒度是“線程”,而不是“調(diào)用”(這與pthread(POSIX線程)互斥體的默認(rèn)加鎖行為不同,pthread互斥體的獲取操作是以“調(diào)用”為粒度的)。

重入進(jìn)一步提升了加鎖行為的封裝性。在java中子類改寫(xiě)父類synchronized方法,然后在其中調(diào)用父類的方法,如果沒(méi)有可重入的鎖,那么這段代碼將產(chǎn)生死鎖。

對(duì)于可能被多個(gè)線程同時(shí)訪問(wèn)的可變狀態(tài)變量,在訪問(wèn)它時(shí)需要持有同一個(gè)鎖,在這種情況下,我們稱狀態(tài)變量是由這個(gè)鎖保護(hù)的。

一種常見(jiàn)的加鎖約定是,將所有的可變狀態(tài)都封裝在對(duì)象內(nèi)部,并通過(guò)對(duì)象的內(nèi)置鎖對(duì)所有訪問(wèn)可變狀態(tài)的代碼路徑進(jìn)行同步,使得在該對(duì)象上不會(huì)發(fā)生并發(fā)訪問(wèn)

對(duì)于包含多個(gè)變量的不變性條件,其中涉及的所有變量都需要由同一個(gè)鎖來(lái)保護(hù)。

如果同步可以避免競(jìng)態(tài)條件的問(wèn)題,那么為什么不在每個(gè)方法聲明時(shí)都是用關(guān)鍵字synchronized?

如果不加區(qū)別的濫用synchronized,可能導(dǎo)致程序中出現(xiàn)過(guò)多的同步如果只是將每個(gè)方法都作為同步方法,那么并不足以確保Vector上復(fù)合操作都是原子的,比如在程序代碼中使用Vector:if(!vector.contains(element)) vector.add(element);

contains和add方法均為syn方法,但是上面這段代碼為先檢查后執(zhí)行,存在競(jìng)態(tài)條件,需要將這兩個(gè)操作合并為復(fù)合操作。

不良并發(fā)(Poor concurrency)應(yīng)用程序:可同時(shí)調(diào)用的數(shù)量不僅受到可用處理資源的限制,還受到應(yīng)用程序本身結(jié)構(gòu)的限制。

縮小同步代碼塊的作用范圍,可以確保程序的并發(fā)性,同時(shí)又維護(hù)線程安全性。但是,如果將同步代碼塊分解的過(guò)細(xì),那么在獲取鎖與釋放鎖等操作上都需要一定的開(kāi)銷(xiāo)。

當(dāng)執(zhí)行時(shí)間較長(zhǎng)的計(jì)算或者可能無(wú)法快速完成的操作時(shí)(例如,網(wǎng)絡(luò)I/O或者控制臺(tái)I/O),一定不要持有鎖。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 临湘市| 建瓯市| 馆陶县| 阳江市| 屯留县| 宁阳县| 珲春市| 武隆县| 伊春市| 琼中| 胶州市| 宜兴市| 海门市| 沙湾县| 南城县| 黔江区| 达州市| 宁远县| 鄂托克旗| 绍兴县| 武平县| 延吉市| 克拉玛依市| 波密县| 兴隆县| 土默特左旗| 名山县| 祥云县| 赤壁市| 佛教| 耿马| 繁昌县| 九江县| 正蓝旗| 美姑县| 兰西县| 若尔盖县| 博兴县| 台北县| 乌兰县| 商水县|