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