成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

線程安全

fuyi501 / 1022人閱讀

摘要:不可變在中,不可變的對象一定是線程安全的。在里標(biāo)注自己是線程安全的類,大多都不是絕對線程安全,比如某些情況下類在調(diào)用端也需要額外的同步措施。無同步方案要保證線程安全,不一定就得需要數(shù)據(jù)的同步,兩者沒有因果關(guān)系。

在之前學(xué)習(xí)編程的時(shí)候,有一個(gè)概念根深蒂固,即程序=算法+數(shù)據(jù)結(jié)構(gòu)。數(shù)據(jù)代表問題空間中的客體,代碼就用來處理這些數(shù)據(jù),這種思維是站在計(jì)算機(jī)的角度去抽象問題和解決問題,稱之為面向過程編程。后來逐漸的發(fā)展,誕生了面向?qū)ο蟮木幊趟枷?。面向?qū)ο笫钦驹诂F(xiàn)實(shí)世界的角度去抽象解決問題,把數(shù)據(jù)和行為都看成對象的一部分。

有了面向?qū)ο蟮木幊棠J?,極大的地提升了現(xiàn)代軟件的開發(fā)效率和規(guī)模,但是現(xiàn)實(shí)世界和計(jì)算機(jī)世界還是有很大的差異。比如人們很難想象在現(xiàn)實(shí)世界中進(jìn)行一項(xiàng)工作的時(shí)候,不停的中斷和切換,某些屬性也會在中斷期間改變,而這些事件在計(jì)算機(jī)里是很正常的。因此不得不妥協(xié),首先在保證數(shù)據(jù)的準(zhǔn)確性之后,才能來談高效。

1 什么叫線程安全

我們談?wù)摰木€程安全,是限定在多個(gè)線程之間存在共享數(shù)據(jù)訪問,因?yàn)槿绻欢未a根本不會與其他線程共享數(shù)據(jù),那也就不會出現(xiàn)線程安全問題。

當(dāng)多個(gè)線程訪問一個(gè)對象的時(shí)候,如果不考慮這些線程在運(yùn)行時(shí)環(huán)境下的調(diào)度和交替執(zhí)行,也不需要進(jìn)行額外的同步,或者在調(diào)用方進(jìn)行任何其他協(xié)調(diào)操作的時(shí)候,調(diào)用這個(gè)對象的行為都可以獲得正確的結(jié)果,那這個(gè)對象就是線程安全的。

也就是當(dāng)一個(gè)對象可以安全的被多個(gè)線程同時(shí)使用,那么它就是線程安全對象。

2 java線程安全

按照線程安全的安全程度來分的話,java中的各種操作共享的數(shù)據(jù)主要分為5類:不可變、絕對線程安全、相對線程安全、線程兼容和線程對立。

2.1 不可變

在java中,不可變(immutable)的對象一定是線程安全的。

對于final關(guān)鍵字可見性來說,只要一個(gè)不可變對象被正確構(gòu)建出來,那其外部的可見狀態(tài)永遠(yuǎn)也不會改變。不可變帶來的安全性是最簡單和最純粹的。

對于基本數(shù)據(jù)類型來說,只需要用final修飾即可。

對于對象來說,將對象中帶有狀態(tài)的變量都設(shè)置為final。

在java api中復(fù)核不可變要求的類型主要有:

String

枚舉類型

Long和Double等數(shù)值包裝類型

BigInteger和BigDecimal等大數(shù)據(jù)類型

AtomicLong和AtomInteger等原子類并非是不可變的類型。

2.2 絕對線程安全

一個(gè)類要達(dá)到“不管運(yùn)行時(shí)環(huán)境如何,調(diào)用者都不需要任何額外的同步措施”這個(gè)條件,通常是需要付出很大,甚至是有些不切實(shí)際的代價(jià)。

在java里標(biāo)注自己是線程安全的類,大多都不是絕對線程安全,比如某些情況下Vector類在調(diào)用端也需要額外的同步措施。

2.3 相對線程安全

這個(gè)就是我們通常意義所說的線程安全。

它需要保證對這個(gè)對象多帶帶的操作時(shí)線程安全的,我們在調(diào)用的時(shí)候不需要做額外的保障措施。但是對于一些特定順序的連續(xù)調(diào)用,就可能需要在調(diào)用端使用額外的同步手段來保證調(diào)用的正確性。

如vector,hashtable等線程安全類都是屬于這種的。

2.4 線程兼容

線程兼容是指對象本身并不是線程安全的,但是可以通過在調(diào)用端正確使用同步手段來保證對象在并發(fā)環(huán)境中可以安全使用。

java中的ArrayList和HashMap就是這種。

2.5 線程對立

指無論調(diào)用端是否采用同步措施,都無法在多線程環(huán)境中并發(fā)使用代碼。

一個(gè)例子就是Thread類的suspen方法和resume方法,如果有兩個(gè)線程同時(shí)持有一個(gè)線程對象,一個(gè)去中斷線程,一個(gè)去恢復(fù)線程,如果并發(fā)進(jìn)行的話,無論調(diào)用是否采用了同步,都會存在鎖死的風(fēng)險(xiǎn)。

常見的線程對立例子還有:

System.setIn()

System.setOut()

System.runFinalizersOnExit()

3 線程安全的實(shí)現(xiàn)方法

如何實(shí)現(xiàn)線程安全與代碼編寫有著很大的關(guān)系,但是虛擬機(jī)提供的同步和鎖的機(jī)制也起到了非常重要的作用。

3.1 互斥同步

互斥同步是一種比較常見的并發(fā)正確性保障手段。

同步是指在多個(gè)線程并發(fā)訪問共享數(shù)據(jù)時(shí),保證共享數(shù)據(jù)在同一時(shí)刻只被一個(gè)(或是一些,使用信號量的時(shí)候是一些)線程使用。互斥是實(shí)現(xiàn)同步的一種手段,臨界區(qū)、互斥量和信號量等都是主要的互斥手段。

synchronized

java中最基本的互斥同步手段就是synchronized關(guān)鍵字。

synchronized關(guān)鍵字在經(jīng)過編譯之后,會在同步塊的前后形成monitorenter和monitorexit這兩個(gè)字節(jié)碼指令。這兩個(gè)字節(jié)碼都需要一個(gè)引用類型的參數(shù)來指明鎖定和解鎖的對象。如果在java程序中指明了這個(gè)對象,那么這個(gè)參數(shù)就是此對象的引用,如果沒有指定,那就根據(jù)synchronized修飾的是實(shí)例方法還是類方法來取對應(yīng)的對象實(shí)例或者Class對象來作為鎖對象。

synchronized同步塊對同一條線程來說是可重入的,不會出現(xiàn)自己把自己鎖死的問題。

synchronized同步塊在已進(jìn)入的線程執(zhí)行完之前,會阻塞后面其他線程的進(jìn)入。

ReentrantLock

java.util.concurrent包中提供了重入鎖來實(shí)現(xiàn)同步。

ReentrantLock在寫的時(shí)候,使用lock()和unlock()方法配合try/finally來完成,相比synchronized增加了一些高級功能:

等待可中斷

當(dāng)持有鎖的線程長期不釋放鎖的時(shí)候,正在等待的線程可以放棄等待,改為處理別的事情。

可實(shí)現(xiàn)公平鎖

公平鎖是指多個(gè)線程在等待一個(gè)同一個(gè)鎖的時(shí)候,必須按照申請鎖的時(shí)間順序來依次獲得鎖,也就是隊(duì)列方式。非公平鎖則是競爭獲取。

synchronized是非公平的,ReentrantLock默認(rèn)也是非公平的,但是可以實(shí)現(xiàn)公平的。

鎖可以綁定多個(gè)事件

一個(gè)ReentrantLock對象可以綁定多個(gè)Condition對象,而synchronized的鎖對象的wait、notify等方法只能實(shí)現(xiàn)一個(gè)隱含的條件。

如果要用到上面三個(gè)高級功能的話,建議使用ReentrantLock,但是如果基于性能考慮的話,優(yōu)先考慮使用synchronized來進(jìn)行同步。

在jdk1.6之前,synchronized在多線程下吞吐量下降很嚴(yán)重,ReentrantLock表現(xiàn)穩(wěn)定。但是1.6之后,性能就差不多了,而且以后虛擬機(jī)的優(yōu)化也是偏向synchronized。

互斥同步最主要的問題就是進(jìn)行線程阻塞和喚醒所帶來的性能問題,因此這種同步也是阻塞同步。從處理問題角度來講,互斥同步是一種悲觀的并發(fā)策略,總是認(rèn)為只要不去做正確的同步措施(例如加鎖),那就肯定會出問題,無論共享數(shù)據(jù)是否會出現(xiàn)競爭,它都會去加鎖同步。

3.2 非阻塞同步

隨著硬件指令集的發(fā)展,出現(xiàn)了基于沖突檢測的樂觀并發(fā)策略。

通俗來講就是,先進(jìn)行操作,如果沒有其他線程爭用共享數(shù)據(jù),那操作就成功了;如果共享數(shù)據(jù)有爭用,產(chǎn)生了沖突,那就再采取其他的補(bǔ)償措施,這種樂觀的并發(fā)策略的許多實(shí)現(xiàn)都不需要把線程掛起,因此這種同步操作稱為非阻塞同步。

最常見的補(bǔ)償措施就是不斷的重試,直到成功為止。

對于非阻塞同步來講,最重要的一個(gè)硬件指令是比較并交換(CAS)。java的Unsafe類里面的某些方法被編譯之后,就成了一條平臺相關(guān)的處理器CAS指令,沒有方法調(diào)用的過程。

Unsafe類不是提供給用戶程序調(diào)用的類,不使用反射的話,只能通過使用其他java api來間接使用。java的concurrent包里的AtomicInteger整數(shù)原子類的compareAndSet和getAndIncrement方法使用了Unsafe類的CAS操作。

CAS操作會出現(xiàn)“ABA”問題:如果一個(gè)變量初始被讀取是A,最終被賦值的時(shí)候檢查到仍然是A,但是在讀取和賦值這段時(shí)間里,有可能被其他線程改為B,后來又改成了A。那么CAS就認(rèn)為沒有改變過。大部分情況下ABA也不會影響程序并發(fā)的正確性。

3.3 無同步方案

要保證線程安全,不一定就得需要數(shù)據(jù)的同步,兩者沒有因果關(guān)系。如果一個(gè)方法不涉及共享數(shù)據(jù),那它自然就不用同步,有些代碼天生就是線程安全的,比如:

可重入代碼

也叫純代碼,可以在代碼執(zhí)行的任何時(shí)刻中斷它,轉(zhuǎn)而去執(zhí)行別的代碼(包括遞歸調(diào)用自己),而控制權(quán)返回后,原來的程序不會出現(xiàn)錯誤。

所有可重入的代碼都是線程安全的,但是線程安全的代碼不一定是可重入的。

可重入代碼的一些特征是:

不依賴存儲在堆上的數(shù)據(jù)和公用系統(tǒng)資源

用的狀態(tài)量都是參數(shù)傳入

不調(diào)用不可重入代碼

如果一個(gè)方法是結(jié)果是可預(yù)測的,只要輸入了相同的數(shù)據(jù),就能返回相同的結(jié)果,那它就滿足可重入性要求,當(dāng)然也就是線程安全的。

線程本地存儲

如果一段代碼中所需要的數(shù)據(jù)必須與其他代碼共享,那就看看這些共享數(shù)據(jù)的代碼能不能在同一個(gè)線程中運(yùn)行?如果把這些共享數(shù)據(jù)的可見范圍放在同一個(gè)線程之內(nèi),這樣無需進(jìn)行同步也可以做到線程安全。

符合這種特點(diǎn)的應(yīng)用有很多,比如:

大部分的消息隊(duì)列的生產(chǎn)者——消費(fèi)者模式。

web交互模型中的一個(gè)請求對應(yīng)一個(gè)服務(wù)器線程的處理方式。

在java中,如果一個(gè)變量要被某個(gè)線程獨(dú)享,就可以用ThreadLocal來實(shí)現(xiàn)線程本地存儲的功能。

4 寫在最后

通過對線程安全的仔細(xì)研究,終于理解了函數(shù)式編程為什么是天然的支持高并發(fā)了。函數(shù)式編程里的不可變對象和可重入代碼,都不會出現(xiàn)線程安全的問題。這也是為什么現(xiàn)在函數(shù)式編程越來越火的一個(gè)重要原因。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/70711.html

相關(guān)文章

  • 第10章:并發(fā)和分布式編程 10.1并發(fā)性和線程安全

    摘要:并發(fā)模塊本身有兩種不同的類型進(jìn)程和線程,兩個(gè)基本的執(zhí)行單元。調(diào)用以啟動新線程。在大多數(shù)系統(tǒng)中,時(shí)間片發(fā)生不可預(yù)知的和非確定性的,這意味著線程可能隨時(shí)暫?;蚧謴?fù)。 大綱 什么是并發(fā)編程?進(jìn)程,線程和時(shí)間片交織和競爭條件線程安全 策略1:監(jiān)禁 策略2:不可變性 策略3:使用線程安全數(shù)據(jù)類型 策略4:鎖定和同步 如何做安全論證總結(jié) 什么是并發(fā)編程? 并發(fā)并發(fā)性:多個(gè)計(jì)算同時(shí)發(fā)生。 在現(xiàn)代...

    instein 評論0 收藏0
  • 什么時(shí)候線程安全?怎樣做到線程安全?怎么擴(kuò)展線程安全的類?

    摘要:什么時(shí)候會出現(xiàn)線程不安全操作并非原子。只有單個(gè)組件,且它是線程安全的。這種情況下,就是的線程安全實(shí)際是委托給了整個(gè)表現(xiàn)出了線程安全。 當(dāng)多個(gè)線程去訪問某個(gè)類時(shí),如果類會表現(xiàn)出我們預(yù)期出現(xiàn)的行為,那么可以稱這個(gè)類是線程安全的。 什么時(shí)候會出現(xiàn)線程不安全? 操作并非原子。多個(gè)線程執(zhí)行某段代碼,如果這段代碼產(chǎn)生的結(jié)果受不同線程之間的執(zhí)行時(shí)序影響,而產(chǎn)生非預(yù)期的結(jié)果,即發(fā)生了競態(tài)條件,就會...

    wh469012917 評論0 收藏0
  • 線程基礎(chǔ)必要知識點(diǎn)!看了學(xué)習(xí)多線程事半功倍

    摘要:是需要我們?nèi)ヌ幚砗芏嗍虑?,為了防止多線程給我們帶來的安全和性能的問題下面就來簡單總結(jié)一下我們需要哪些知識點(diǎn)來解決多線程遇到的問題。 前言 不小心就鴿了幾天沒有更新了,這個(gè)星期回家咯。在學(xué)校的日子要努力一點(diǎn)才行! 只有光頭才能變強(qiáng) 回顧前面: 多線程三分鐘就可以入個(gè)門了! Thread源碼剖析 本文章的知識主要參考《Java并發(fā)編程實(shí)戰(zhàn)》這本書的前4章,這本書的前4章都是講解并發(fā)的基...

    YPHP 評論0 收藏0
  • Java 線程相關(guān)類

    摘要:提供了線程安全的共享對象,在編寫多線程代碼時(shí),可把不安全的整個(gè)變量封裝進(jìn),或者把該對象與線程相關(guān)的狀態(tài)使用保存并不能替代同步機(jī)制,兩者面向的問題領(lǐng)域不同。 ThreadLocal類 使用ThreadLocal類可以簡化多線程編程時(shí)的并發(fā)訪問,使用這個(gè)工具類可以很簡捷地隔離多線程程序的競爭資源。Java5之后,為ThreadLocal類增加了泛型支持,即ThreadLocal Threa...

    Sanchi 評論0 收藏0
  • 如何看待Spring下單例模式與線程安全的矛盾

    摘要:線程安全問題都是由全局變量及靜態(tài)變量引起的。常量始終是線程安全的,因?yàn)橹淮嬖谧x操作。局部變量是線程安全的。有狀態(tài)對象,就是有實(shí)例變量的對象,可以保存數(shù)據(jù),是非線程安全的。 前言 有多少人在使用Spring框架時(shí),很多時(shí)候不知道或者忽視了多線程的問題? ??因?yàn)閷懗绦驎r(shí),或做單元測試時(shí),很難有機(jī)會碰到多線程的問題,因?yàn)闆]有那么容易模擬多線程測試的環(huán)境。那么當(dāng)多個(gè)線程調(diào)用同一個(gè)bean的時(shí)...

    dinfer 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<