多線程死鎖的產生的原因以及如何避免死鎖出現


1,死鎖產生的原因

  1) 系統資源競爭

   通常系統中擁有的多個不可剝奪資源,其數量不足以滿足多個進程運行的需要,使得進程再運行過程中,會因為爭奪資源而陷入僵局,如磁帶機,打印機等,只有對可不可剝奪資源的競爭,才會產生死鎖,對可剝奪資源的競爭是不會引起死鎖的

  2)進程推進順序非法

   進程再運行過程中,請求和釋放資源的順續不當,導致死鎖

2,死鎖產生的必要條件

  產生死鎖必須同時滿足四個條件,只要其中一個條件不成立,死鎖就不會發生

  1)互斥條件:進程要求對所分配的資源進行排他型控制,即在一段時間內,某個資源僅為一個進程所占有

  2)不剝奪條件:進程所獲得的資源在未使用完畢之前,不能被其他進程強行奪走,即只能 由獲得該資源的進程自己來主動釋放

  3)請求和保持條件:進程已經保持了至少一個資源,但又提出了新的資源請求,而該資源 已被其他進程占有,此時請求進程被阻塞,但對自己已獲得的資源保持不放

  4)循環等待條件:存在一種進程資源的循環等待鏈,鏈中每一個進程已獲得的資源同時被 鏈中下一個進程所請求。即存在一個處於等待狀態的進程集合{Pl, P2, ..., pn},其中Pi等 待的資源被P(i+1)占有(i=0, 1, ..., n-1),Pn等待的資源被P0占有,如圖2-15所示

  直觀上看,循環等待條件似乎和死鎖的定義一樣,其實不然。按死鎖定義構成等待環所 要求的條件更嚴,它要求Pi等待的資源必須由P(i+1)來滿足,而循環等待條件則無此限制。 例如,系統中有兩台輸出設備,P0占有一台,PK占有另一台,且K不屬於集合{0, 1, ..., n}。

  Pn等待一台輸出設備,它可以從P0獲得,也可能從PK獲得。因此,雖然Pn、P0和其他 一些進程形成了循環等待圈,但PK不在圈內,若PK釋放了輸出設備,則可打破循環等待, 如圖2-16所示。因此循環等待只是死鎖的必要條件。

  資源分配圖含圈系統不一定有死鎖的原因是同類資源數大於1。但若系統中每類資 源都只有一個資源,則資源分配圖含圈系統就變成了系統出現死鎖的充分必要條件。

 

3,如何避免死鎖

  1)加鎖順序:當多個線程需要相同的一些鎖,但是按照不同的順序加鎖,死鎖就很容易發生,如果能確保所有的線程都是按照相同的順序獲得鎖,那么死鎖就不會發生

       缺點:按照順序加鎖是一種有效的死鎖預防機制。但是,這種方式需要你事先知道所有可能會用到的鎖

  2)加鎖時限:兩個線程在嘗試獲取鎖的時候加一個超時時間,這也就意味着在嘗試獲取鎖的過程中若超過了這個時限該線程則放棄對該鎖請求。若一個線程沒有在給定的時限內成功獲得所有需要的鎖,則會進行回退並釋放所有已經獲得的鎖,然后等待一段隨機的時間再重試。

  缺點:非常多的線程同一時間去競爭同一批資源,就算有超時和回退機制,還是可能會導致這些線程重復地嘗試但卻始終得不到鎖。如果只有兩個線程,並且重試的超時時間設定為0到500毫秒之間,這種現象可能不會發生,但是如果是10個或20個線程情況就不同了。因為這些線程等待相等的重試時間的概率就高的多

  超時和重試機制是為了避免在同一時間出現的競爭,這種機制存在一個問題,在Java中不能對synchronized同步塊設置超時時間。你需要創建一個自定義鎖,或使用Java5中java.util.concurrent包下的工具。寫一個自定義鎖類不復雜,但超出了本文的內容。后續的Java並發系列會涵蓋自定義鎖的內容。

  3)死鎖檢測

每當一個線程獲得了鎖,會在線程和鎖相關的數據結構中(map、graph等等)將其記下。除此之外,每當有線程請求鎖,也需要記錄在這個數據結構中。

當一個線程請求鎖失敗時,這個線程可以遍歷鎖的關系圖看看是否有死鎖發生。例如,線程A請求鎖7,但是鎖7這個時候被線程B持有,這時線程A就可以檢查一下線程B是否已經請求了線程A當前所持有的鎖。如果線程B確實有這樣的請求,那么就是發生了死鎖(線程A擁有鎖1,請求鎖7;線程B擁有鎖7,請求鎖1)。

當然,死鎖一般要比兩個線程互相持有對方的鎖這種情況要復雜的多。線程A等待線程B,線程B等待線程C,線程C等待線程D,線程D又在等待線程A。線程A為了檢測死鎖,它需要遞進地檢測所有被B請求的鎖。從線程B所請求的鎖開始,線程A找到了線程C,然后又找到了線程D,發現線程D請求的鎖被線程A自己持有着。這是它就知道發生了死鎖。

下面是一幅關於四個線程(A,B,C和D)之間鎖占有和請求的關系圖。像這樣的數據結構就可以被用來檢測死鎖。

那么當檢測出死鎖時,這些線程該做些什么呢?

一個可行的做法是釋放所有鎖,回退,並且等待一段隨機的時間后重試。這個和簡單的加鎖超時類似,不一樣的是只有死鎖已經發生了才回退,而不會是因為加鎖的請求超時了。雖然有回退和等待,但是如果有大量的線程競爭同一批鎖,它們還是會重復地死鎖(編者注:原因同超時類似,不能從根本上減輕競爭)。

一個更好的方案是給這些線程設置優先級,讓一個(或幾個)線程回退,剩下的線程就像沒發生死鎖一樣繼續保持着它們需要的鎖。如果賦予這些線程的優先級是固定不變的,同一批線程總是會擁有更高的優先級。為避免這個問題,可以在死鎖發生的時候設置隨機的優先級。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM