什么是死鎖以及避免死鎖_第1頁(yè)
什么是死鎖以及避免死鎖_第2頁(yè)
什么是死鎖以及避免死鎖_第3頁(yè)
什么是死鎖以及避免死鎖_第4頁(yè)
全文預(yù)覽已結(jié)束

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、什么是死鎖以及避免死鎖、定義線程死鎖是指由于兩個(gè)或者多個(gè)線程互相持有對(duì)所需要的資源,導(dǎo)致這些線程處于等待狀態(tài),法前往執(zhí)。當(dāng)線程進(jìn)對(duì)象的synchronized代碼塊時(shí),便占有了資源,直到它退出該代碼塊或者調(diào)wait法,才釋放資源,在此期間,其他線程將不能進(jìn)該代碼塊。當(dāng)線程互相持有對(duì)所需要的資源時(shí),會(huì)互相等待對(duì)釋放資源,如果線程都不主動(dòng)釋放所占有的資源,將產(chǎn)死鎖。當(dāng)然死鎖的產(chǎn)是必須要滿些特定條件的:互斥條件:進(jìn)程對(duì)于所分配到的資源具有排它性,即個(gè)資源只能被個(gè)進(jìn)程占,直到被該進(jìn)程釋放請(qǐng)求和保持條件:個(gè)進(jìn)程因請(qǐng)求被占資源發(fā)阻塞時(shí),對(duì)已獲得的資源保持不放。不剝奪條件:任何個(gè)資源在沒(méi)被該進(jìn)程釋放之前,任

2、何其他進(jìn)程都法對(duì)他剝奪占循環(huán)等待條件:當(dāng)發(fā)死鎖時(shí),所等待的進(jìn)程必定會(huì)形成個(gè)環(huán)路(類似于死循環(huán)),造成永久阻塞。package com.sxy.thread;/*線程Thread1率先占有了resource1,繼續(xù)運(yùn)時(shí)需要resource2,但此時(shí)resource2卻被線程Thread2占有了,*因此只能等待Thread2釋放resource2才能夠繼續(xù)運(yùn);同時(shí),Thread2也需要resource1,*它只能等待Thread1釋放resource1才能夠繼續(xù)運(yùn),因此,Thread1和Thread2都處于等待狀態(tài),*誰(shuí)也法繼續(xù)運(yùn),即產(chǎn)了死鎖。* author sunxy*/public clas

3、s DeadLock public static void main(String args) dead_lock();private static void dead_lock() /兩個(gè)資源final Object resource1 = resource1;final Object resource2 = resource2;/第個(gè)線程,想先占有resource1,再嘗試著占有resource2Thread t1 = new Thread() public void run() /嘗試占有resource1synchronized (resource1) /成功占有resource1Sy

4、stem.out.println(Thread1 1:locked resource1);/休眠段時(shí)間try Thread.sleep(50); catch (InterruptedException e) e.printStackTrace();/嘗試占有resource2,如果不能占有,該線程會(huì)直等到synchronized (resource2) System.out.println(Thread1 1:locked resource2);/第個(gè)線程,想先占有resource2,再占有resource1Thread t2 = new Thread() public void run()

5、/嘗試占有resource2synchronized (resource2) /成功占有resource2System.out.println(Thread 2 :locked resource2);/休眠段時(shí)間try Thread.sleep(50); catch (InterruptedException e) e.printStackTrace();/嘗試占有resource1,如果不能占有,該線程會(huì)直等到synchronized (resource1) System.out.println(Thread1 2:locked resource1);/啟動(dòng)線程t1.start();t2.s

6、tart();死鎖的另種:遞歸死鎖,舉例:所謂遞歸函數(shù)就是調(diào)函數(shù),在函數(shù)體內(nèi)直接或間接的調(diào),即函數(shù)的嵌套是函數(shù)本。遞歸式有兩種直接遞歸和間接遞歸,直接遞歸就是在函數(shù)中出現(xiàn)調(diào)函數(shù)本。間接遞歸,指函數(shù)中調(diào)了其他函數(shù),該其他函數(shù)調(diào)了本函數(shù)。那什么時(shí)候使遞歸呢?般來(lái)說(shuō)當(dāng)你要在某段代碼邏輯中使循環(huán)迭代的時(shí)候但是迭代的次數(shù)在迭代之前法知曉的情況下使遞歸。打個(gè)你要在個(gè)件夾中查找某個(gè)件,這個(gè)件夾底下有N多件夾和件,當(dāng)你在不知道有多少層件夾和件的情況下你就得到遞歸了。遞歸的優(yōu)點(diǎn)就是讓代碼顯得很簡(jiǎn)潔,同時(shí)有些應(yīng)場(chǎng)景不得不使遞歸如前說(shuō)的找件。遞歸是個(gè)好東西但是在某些時(shí)候也會(huì)給你帶來(lái)些煩。如在多線程的環(huán)境下使遞歸,遇

7、到了多線程那么就不得不對(duì)同步的問(wèn)題。遞歸程序遇到同步的時(shí)候很容易出問(wèn)題。多線程的遞歸就是指遞歸鏈中的某個(gè)法由另外個(gè)線程來(lái)操作,以下代碼的意思都是這個(gè)意思即調(diào)recursive()和businessLogic()并個(gè)線程如果是在個(gè)線程中就不存在死鎖問(wèn)題,例如下的recursive變成private就不存在問(wèn)題。)java1. public class Test .6.7.public void recursive()this.businessLogic();public synchronized void businessLogic()System.out.println(處理業(yè)務(wù)邏輯);保存到

8、a href=/base/mysql class=replace_word title=MySQL知識(shí)庫(kù) target=_blank style=color:#df3434; 數(shù)據(jù)庫(kù));8.9.this.recursive();10. 以上這段代碼就是個(gè)能形成死鎖的代碼,事實(shí)上這個(gè)“synchronized”放在“businessLogic()”和“recursive()”都會(huì)形成死鎖,并且是多線程的情況下就會(huì)鎖住!他的邏輯順序是先執(zhí)recursive()法然后接下來(lái)執(zhí)businessLogic()法同時(shí)將businessLogic()法鎖住,接下來(lái)程序進(jìn)businessLogic()法內(nèi)部執(zhí)

9、完打印語(yǔ)句后開(kāi)始執(zhí)recursive(),進(jìn)recursive()后準(zhǔn)備執(zhí)businessLogic(),等等問(wèn)題來(lái)了!之前執(zhí)的businessLogic()的鎖還沒(méi)有放開(kāi)這次執(zhí)到這了,當(dāng)然是過(guò)不去的了,形成了死鎖!從這個(gè)例我們總結(jié)出來(lái)個(gè)規(guī)律就是在遞歸的時(shí)候在遞歸鏈上的法上加鎖肯定會(huì)出現(xiàn)死鎖(所謂遞歸鏈就是指recursive()鏈向businessLogic(),businessLogic()鏈回recursive()),解決這個(gè)問(wèn)題的法就是避免在遞歸鏈上加鎖,請(qǐng)看以下的例java1. public class Test 2.3.public void recursive()this.bus

10、inessLogic();4.5.public void businessLogic()System.out.println(處理業(yè)務(wù)邏輯);this.saveToDB();6.7.8.this.recursive();9.10.11.public synchronized void saveToDB()System.out.println(保存到數(shù)據(jù)庫(kù));12.13. saveToDB()不在這條遞歸鏈上然不會(huì)出現(xiàn)死鎖,所以說(shuō)在遞歸中加鎖是件很危險(xiǎn)的事情,實(shí)在逃不過(guò)要加鎖就加在最的粒度的程序代碼上以減死鎖的概率。避免死鎖:在有些情況下死鎖是可以避免的。本將展三種于避免死鎖的技術(shù):1.2.3.

11、加鎖順序當(dāng)多個(gè)線程需要相同的些鎖,但是按照不同的順序加鎖,死鎖就很容易發(fā)。如果能確保所有的線程都是按照相同的順序獲得鎖,那么死鎖就不會(huì)發(fā)??聪逻@個(gè)例:Thread 1:lock Alock BThread 2:wait for Alock C (when A locked)Thread 3:wait for Await for Bwait for C如果個(gè)線程(如線程3)需要些鎖,那么它必須按照確定的順序獲取鎖。它只有獲得了從順序上排在前的鎖之后,才能獲取后的鎖。例如,線程2和線程3只有在獲取了鎖A之后才能嘗試獲取鎖C(譯者注:獲取鎖A是獲取鎖C的必要條件。因?yàn)榫€程1已經(jīng)擁有了鎖A,所以線程2

12、和3需要直等到鎖A被釋放。然后在它們嘗試對(duì)B或C加鎖之前,必須成功地對(duì)A加了鎖。按照順序加鎖是種有效的死鎖預(yù)防機(jī)制。但是,這種式需要你事先知道所有可能會(huì)到的鎖譯者注:并對(duì)這些鎖做適當(dāng)?shù)呐判?,但總有些時(shí)候是法預(yù)知的。加鎖時(shí)限另外個(gè)可以避免死鎖的法是在嘗試獲取鎖的時(shí)候加個(gè)超時(shí)時(shí)間,這也就意味著在嘗試獲取鎖的過(guò)程中若超過(guò)了這個(gè)時(shí)限該線程則放棄對(duì)該鎖請(qǐng)求。若個(gè)線程沒(méi)有在給定的時(shí)限內(nèi)成功獲得所有需要的鎖,則會(huì)進(jìn)回退并釋放所有已經(jīng)獲得的鎖,然后等待段隨機(jī)的時(shí)間再重試。這段隨機(jī)的等待時(shí)間讓其它線程有機(jī)會(huì)嘗試獲取相同的這些鎖,并且讓該應(yīng)在沒(méi)有獲得鎖的時(shí)候可以繼續(xù)運(yùn)譯者注:加鎖超時(shí)后可以先繼續(xù)運(yùn)點(diǎn)其它事情,再回

13、頭來(lái)重復(fù)之前加鎖的邏輯以下是個(gè)例,展了兩個(gè)線程以不同的順序嘗試獲取相同的兩個(gè)鎖,在發(fā)超時(shí)后回退并重試的場(chǎng)景:Thread 1 locks AThread 2 locks BThread 1 attempts to lock B but is blockedThread 2 attempts to lock A but is blockedThread 1s lock attempt on B times outThread 1 backs up and releases A as wellThread 1 waits randomly (e.g. 257 millis) before retr

14、ying.Thread 2s lock attempt on A times outThread 2 backs up and releases B as wellThread 2 waits randomly (e.g. 43 millis) before retrying.在上的例中,線程2線程1早200毫秒進(jìn)重試加鎖,因此它可以先成功地獲取到兩個(gè)鎖。這時(shí),線程1嘗試獲取鎖A并且處于等待狀態(tài)。當(dāng)線程2結(jié)束時(shí),線程1也可以順利的獲得這兩個(gè)鎖(除線程2或者其它線程在線程1成功獲得兩個(gè)鎖之前獲得其中的些鎖)。需要注意的是,由于存在鎖的超時(shí),所以我們不能認(rèn)為這種場(chǎng)景就定是出現(xiàn)了死鎖。也可能是因?yàn)楂@

15、得了鎖的線程(導(dǎo)致其它線程超時(shí))需要很長(zhǎng)的時(shí)間去完成它的任務(wù)。此外,如果有常多的線程同時(shí)間去競(jìng)爭(zhēng)同批資源,就算有超時(shí)和回退機(jī)制,還是可能會(huì)導(dǎo)致這些線程重復(fù)地嘗試但卻始終得不到鎖。如果只有兩個(gè)線程,并且重試的超時(shí)時(shí)間設(shè)定為0到500毫秒之間,這種現(xiàn)象可能不會(huì)發(fā),但是如果是10個(gè)或20個(gè)線程情況就不同了。因?yàn)檫@些線程等待相等的重試時(shí)間的概率就的多(或者常接近以于會(huì)出現(xiàn)問(wèn)題)。譯者注:超時(shí)和重試機(jī)制是為了避免在同時(shí)間出現(xiàn)的競(jìng)爭(zhēng),但是當(dāng)線程很多時(shí),其中兩個(gè)或多個(gè)線程的超時(shí)時(shí)間樣或者接近的可能性就會(huì)很,因此就算出現(xiàn)競(jìng)爭(zhēng)導(dǎo)致超時(shí)后,由于超時(shí)時(shí)間樣,它們會(huì)同時(shí)開(kāi)始重試,導(dǎo)致新輪的競(jìng)爭(zhēng),帶來(lái)了新的問(wèn)) 題。這

16、種機(jī)制存在個(gè)問(wèn)題,在Java中不能對(duì)synchronized同步塊設(shè)置超時(shí)時(shí)間。你需要?jiǎng)?chuàng)建個(gè)定義鎖,或使Java5中java.util.concurrent包下的具。寫(xiě)個(gè)定義鎖類不復(fù)雜,但超出了本的內(nèi)容。后續(xù)的Java并發(fā)系列會(huì)涵蓋定義鎖的內(nèi)容。死鎖檢測(cè)死鎖檢測(cè)是個(gè)更好的死鎖預(yù)防機(jī)制,它主要是針對(duì)那些不可能實(shí)現(xiàn)按序加鎖并且鎖超時(shí)也不可的場(chǎng)景。每當(dāng)個(gè)線程獲得了鎖,會(huì)在線程和鎖相關(guān)的數(shù)據(jù)結(jié)構(gòu)中(map、graph等等)將其記下。除此之外,每當(dāng)有線程請(qǐng)求鎖,也需要記錄在這個(gè)數(shù)據(jù)結(jié)構(gòu)中。當(dāng)個(gè)線程請(qǐng)求鎖失敗時(shí),這個(gè)線程可以遍歷鎖的關(guān)系圖看看是否有死鎖發(fā)。例如,線程A請(qǐng)求鎖7,但是鎖7這個(gè)時(shí)候被線程B持有,這時(shí)線程A就可以檢查下線程B是否已經(jīng)請(qǐng)求了線程A當(dāng)前所持有的鎖。如果線程B確實(shí)有這樣的請(qǐng)求,那么就是發(fā)了死鎖(線程A擁有鎖1,請(qǐng)求鎖7;線程B擁有鎖7,請(qǐng)求鎖1)。當(dāng)然,死鎖般要兩個(gè)線程互相持有對(duì)的鎖這種情況要復(fù)雜的多。線程A等待線程B,線程B等待線程C,線程C等待線程D,線程D在等待線程A。線程A為了檢測(cè)死鎖,它需要遞進(jìn)地檢測(cè)所有被B請(qǐng)求的鎖。從線程B所請(qǐng)求的鎖開(kāi)始,線程A找到了線程C,然后找到了線程D,發(fā)現(xiàn)線程D請(qǐng)求的鎖被線程A持有著。這是它就知道發(fā)了死鎖。下是幅關(guān)于四個(gè)線程(A,B,C和D)之間鎖占有和請(qǐng)求的關(guān)系圖。像這樣的數(shù)據(jù)結(jié)構(gòu)就可以被來(lái)檢測(cè)

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論