摘要:第二章線程安全性線程安全性的理解定義某個(gè)類(lèi)的行為與其規(guī)范完全一致原子性競(jìng)態(tài)條件理解當(dāng)操作的正確的結(jié)果取決于多個(gè)線程的交替執(zhí)行時(shí)序,就會(huì)發(fā)生競(jìng)態(tài)條件。
第二章 線程安全性 2.1 線程安全性的理解
定義:某個(gè)類(lèi)的行為與其規(guī)范完全一致
2.2 原子性 2.2.1 競(jìng)態(tài)條件理解:當(dāng)操作的正確的結(jié)果取決于多個(gè)線程的交替執(zhí)行時(shí)序,就會(huì)發(fā)生競(jìng)態(tài)條件。常見(jiàn)的競(jìng)態(tài)條件類(lèi)型是
”先檢查后執(zhí)行“,首先觀察到某個(gè)條件為真再去采取下一步的動(dòng)作(然而在這兩個(gè)操作之間其實(shí)觀察結(jié)果可能失效)
一個(gè)很常見(jiàn)的例子就是延遲初始化。當(dāng)一個(gè)線程根據(jù)觀察結(jié)果為空進(jìn)行初始化時(shí),一個(gè)線程可能已經(jīng)建立了初始化。
@NotThreadSafe public class LazyInitRace { private SampleClass sample; public SampleClass getInstance() { if (instance == null) { instance = new SampleClass(); } return instance; } }2.2.3 復(fù)合操作
原子操作:對(duì)于訪問(wèn)同一個(gè)狀態(tài)的所有操作(包括該操作本身)來(lái)說(shuō),這個(gè)操作是以一個(gè)原子方式來(lái)執(zhí)行的操作,也即不可分割的操作。如這些操作:先檢查后執(zhí)行,讀取-修改-寫(xiě)入等復(fù)合操作,包含了一組必須以原子方式執(zhí)行的操作以確保其線程安全性。
2.3 加鎖機(jī)制線程安全性的定義中要求,多個(gè)線程之間無(wú)論采取何種執(zhí)行時(shí)序或交替方式,都要保證不變性條件不被破壞。當(dāng)在不變性條件中涉及多個(gè)策略時(shí),各個(gè)變量之間不是彼此獨(dú)立的話,某個(gè)變量的值就會(huì)對(duì)其他變量的值產(chǎn)生約束。要保持狀態(tài)的一致性,就需要在單個(gè)原子操作中更新所有相關(guān)的狀態(tài)變量。
2.3.1 內(nèi)置鎖Java提供的一種內(nèi)置鎖機(jī)制:同步代碼塊。同步代碼塊包括兩個(gè)部分:有個(gè)作為鎖的對(duì)象引用,一個(gè)作為由這個(gè)鎖保護(hù)的代碼塊,常見(jiàn)的有synchronized關(guān)鍵字:
synchronized (lock) { // TODO }
附:
每個(gè)Java對(duì)象都可以用作一個(gè)實(shí)現(xiàn)同步的鎖,這些鎖被稱為內(nèi)置鎖或監(jiān)視器鎖。
當(dāng)某個(gè)線程請(qǐng)求一個(gè)由其他線程持有的鎖時(shí),發(fā)出請(qǐng)求的線程就會(huì)被阻塞。而如果這個(gè)線程試圖獲取已經(jīng)由它自己持有的鎖,則這個(gè)請(qǐng)求就會(huì)成功。否則,就會(huì)導(dǎo)致死鎖的發(fā)生。簡(jiǎn)單示例如下:
public class Parent { public synchronized void parentMethod { ... } } public class Child extends Parent { public synchronized void childMethod { ... } }2.4 用鎖來(lái)保護(hù)狀態(tài)
首先了解一個(gè)概念,共享狀態(tài):這里指各個(gè)線程共享的有狀態(tài)對(duì)象等。通過(guò)鎖來(lái)構(gòu)造一些協(xié)議,以實(shí)現(xiàn)對(duì)共享狀態(tài)的獨(dú)占訪問(wèn),只要始終遵循這些協(xié)議,就能確保狀態(tài)的一致性。
當(dāng)復(fù)合操作訪問(wèn)共享狀態(tài)時(shí),如讀取-修改-寫(xiě)入,必須是原子操作以避免產(chǎn)生競(jìng)態(tài)條件。
還需要理解的幾個(gè)點(diǎn):1、僅僅將復(fù)合操作封裝到一個(gè)同步代碼塊中是不夠的。如果用同步來(lái)協(xié)調(diào)對(duì)某個(gè)變量的訪問(wèn),那么在訪問(wèn)這個(gè)變量的所有位置上都需要同步。2、當(dāng)使用鎖來(lái)協(xié)調(diào)對(duì)某個(gè)變量的訪問(wèn)時(shí),在訪問(wèn)變量的所有位置上都要使用同一個(gè)鎖。3、對(duì)于可能被多個(gè)線程同時(shí)訪問(wèn)的可變狀態(tài)變量,在訪問(wèn)它時(shí)都需要持有同一個(gè)鎖,在這種情況下,我們稱狀態(tài)變量是由這個(gè)鎖來(lái)保護(hù)的。4、每個(gè)共享的和可變的變量都應(yīng)該只有一個(gè)鎖來(lái)保護(hù),從而使維護(hù)人員知道是哪一個(gè)鎖。
一種加鎖的約定是:使所有的可變狀態(tài)都封裝在對(duì)象內(nèi)部,并通過(guò)對(duì)象的內(nèi)置鎖對(duì)所有訪問(wèn)可變狀態(tài)的代碼路徑·進(jìn)行同步,使得該對(duì)象上不會(huì)發(fā)生同步,如Vector類(lèi)等。
當(dāng)類(lèi)的不變性涉及到多個(gè)狀態(tài)變量時(shí),那么還有另外一個(gè)需求:在不變性條件中的每個(gè)變量都必須由同一個(gè)鎖來(lái)保護(hù)。
不良并發(fā)程序:可同時(shí)調(diào)用的數(shù)量,不僅受到可用處理資源的限制,還受到應(yīng)用程序本身的限制。一種改良做法是(書(shū)中給出的),要確保同步代碼塊不要過(guò)小,且不要將本應(yīng)是原子的操作拆分到多個(gè)同步代碼中。應(yīng)盡量將不影響共享狀態(tài)且執(zhí)行時(shí)間較長(zhǎng)的操作從同步代碼中分離出去,從而在這些操作的執(zhí)行過(guò)程中,其他線程可以訪問(wèn)共享狀態(tài)。
public class CachedFactorizer implements Servlet { private BigInteger lastNumber; private BigInteger[] lastFactors; private long hits; private long cacheHits; public synchronized long getHits() { return hits; } public synchronized double getCachedHits() { return (double)cacheHits / (double)hits; } public void service(ServletRequest req, ServletReponse resp) { BigInteger i = extractFromRequest(req); BigInteger[] factors = null; synchronized(this) { ++hits; if (i.equals(lastNumber)) { ++cacheHits; factors = lastFactors.clone(); } } if (factors == null) { factors = getFactor(i); synchronized(this) { lastNumber = i; lastFactors = factors.clone(); } } encodeIntoResponse(resp, factors); } }
要判斷同步代碼的和李大霄,需要在各個(gè)設(shè)計(jì)需求之間進(jìn)行權(quán)衡,包括安全性、簡(jiǎn)單性和性能。如果持有鎖的時(shí)間過(guò)長(zhǎng),那么會(huì)帶來(lái)活躍性或性能問(wèn)題:當(dāng)執(zhí)行時(shí)間較長(zhǎng)的計(jì)算或者可能無(wú)法快速完成的操作時(shí)(例如,網(wǎng)絡(luò)I/O或控制態(tài)I/O,一定不要持有鎖。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/68321.html
摘要:在之前的文章中學(xué)習(xí)了關(guān)鍵字,可以保證變量在線程間的可見(jiàn)性,但他不能真正的保證線程安全。線程執(zhí)行到指令時(shí),將會(huì)嘗試獲取對(duì)象所對(duì)應(yīng)的的所有權(quán),即嘗試獲得對(duì)象的鎖。從可見(jiàn)性上來(lái)說(shuō),線程通過(guò)持有鎖的方式獲取變量的最新值。 在之前的文章中學(xué)習(xí)了volatile關(guān)鍵字,volatile可以保證變量在線程間的可見(jiàn)性,但他不能真正的保證線程安全。 /** * @author cenkailun *...
摘要:前言文章介紹了單例模式五種實(shí)現(xiàn)的方式,分別是懶漢,餓漢,靜態(tài)內(nèi)部類(lèi),雙重檢驗(yàn)鎖以及枚舉實(shí)現(xiàn)方式,并主要關(guān)心加載時(shí)機(jī)以及線程安全。 前言 文章介紹了單例模式五種實(shí)現(xiàn)的方式,分別是懶漢,餓漢,靜態(tài)內(nèi)部類(lèi),雙重檢驗(yàn)鎖以及枚舉實(shí)現(xiàn)方式,并主要關(guān)心加載時(shí)機(jī)以及線程安全。首先,通俗點(diǎn)講,餓漢就是這個(gè)類(lèi)還沒(méi)被使用到的時(shí)候,實(shí)例已經(jīng)創(chuàng)建好了;而懶漢是使用到的時(shí)候才創(chuàng)建對(duì)應(yīng)的實(shí)例。線程安全方面主要考慮實(shí)...
摘要:線程的基本狀態(tài)線程的基本操作與內(nèi)存模型線程組守護(hù)線程線程優(yōu)先級(jí)線程安全與隱蔽錯(cuò)誤線程的基本狀態(tài)線程的生命周期線程的基本操作新建線程終止線程立即終止線程所有活動(dòng)方法在結(jié)束線程時(shí)會(huì)直接終止線程并立即釋放這個(gè)線程所持有的鎖可能引起數(shù)據(jù)不一致強(qiáng)烈建 1.線程的基本狀態(tài) 2.線程的基本操作 3. volatile與java內(nèi)存模型 4.線程組 5.守護(hù)線程(Daemon) ...
摘要:源碼和多線程安全問(wèn)題分析在分析線程安全問(wèn)題之前,我們線對(duì)此類(lèi)的源碼進(jìn)行分析,找出可能出現(xiàn)線程安全問(wèn)題的地方,然后代碼進(jìn)行驗(yàn)證和分析。即當(dāng)多線程調(diào)用方法的時(shí)候會(huì)出現(xiàn)元素覆蓋的問(wèn)題。 1.ArrayList源碼和多線程安全問(wèn)題分析 在分析ArrayList線程安全問(wèn)題之前,我們線對(duì)此類(lèi)的源碼進(jìn)行分析,找出可能出現(xiàn)線程安全問(wèn)題的地方,然后代碼進(jìn)行驗(yàn)證和分析。 1.1 數(shù)據(jù)結(jié)構(gòu) ArrayLi...
摘要:如果需要防范這種攻擊,請(qǐng)修改構(gòu)造函數(shù),使其在被要求創(chuàng)建第二個(gè)實(shí)例時(shí)拋出異常。單例模式與單一職責(zé)原則有沖突。源碼地址參考文獻(xiàn)設(shè)計(jì)模式之禪 定義 單例模式是一個(gè)比較簡(jiǎn)單的模式,其定義如下: 保證一個(gè)類(lèi)僅有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。 或者 Ensure a class has only one instance, and provide a global point of ac...
閱讀 3335·2021-11-25 09:43
閱讀 3022·2021-10-15 09:43
閱讀 1977·2021-09-08 09:36
閱讀 2930·2019-08-30 15:56
閱讀 757·2019-08-30 15:54
閱讀 2697·2019-08-30 15:54
閱讀 2988·2019-08-30 11:26
閱讀 1258·2019-08-29 17:27