你真的理解什麼是死鎖嗎?

一. 死鎖的概念

兩個或多個進程,由於資源的競爭或者彼此間的通信而造成的阻塞現象,如果沒有外力干預,它們將無法進行下去,這就發生了死鎖。

更規範的定義:集合中的每一個進程都在等待只能由本集合中的其他進程才能引發的事件(資源),那麼該組進程是死鎖的。

上面說到的競爭的資源可以是一切能稱爲資源的東西,比如鎖,網絡連接,通知事件,磁盤等

舉個例子

比方說有兩個線程,a 和 b

  • a 線程持有鎖 a,等待鎖 b
  • b 線程持有鎖 b,等待鎖 a

這樣這兩個線程就出現了死鎖

二. 產生死鎖的必要條件

  • 互斥條件:一個資源一次只能被一個進程訪問;一旦分配給某個進程,其他進程就不能再訪問(因爲競爭資源的被佔用是發生死鎖的一個重要原因)
  • 請求和保持條件:進程在等待其他線程佔用的資源(請求),與此同時,進程會一直佔用着自己已經獲得的資源(保持)
  • 不可剝奪條件:進程對於已經申請到的資源在使用完成之前不可以被剝奪(也就是不會被其他進程搶走自己的資源)
  • 環路等待條件:發生死鎖的進程組中,每一個進程都會佔有另一個進程所需要的資源,這個佔有關係可以形成一個等待環路(也就是競爭資源被相互佔用,無法找到突破口,只能被死死堵着)

三. 如何預防和避免死鎖

1. 以特定的順序獲取資源

以上面的例子來說,A 線程先嚐試獲取 a 鎖再嘗試獲取 b 鎖;而 B 線程則相反。這樣子的設計就很可能出現死鎖。

如果一組線程都按照同樣的順序來嘗試獲得鎖,那麼就可以避免死鎖的發生了。

2. 超時放棄

設置佔有資源或者佔有鎖的最大時間,如果超過該時間仍未釋放,那麼就主動釋放該資源

3. 預先分配資源

在進程運行之前一次性的將其所需(申請)的資源分配給他,保證其可以正常的執行完畢,不需要在進程運行時再申請資源。

該方法的弊端體現在資源利用率低的方面,因爲並不是所有的資源在進程運行時都會被長時間佔用,但是這樣在運行前一次性分配的方式會造成所有資源在該進程的生命週期內都是不可以被釋放的。

4. 銀行家算法

銀行家算法是一種直接避免死鎖的方式,主要的思想就是動態檢查進程對資源的申請,以檢查是否會造成死鎖。

我們通過列出各個線程(進程)的當前佔有的資源,最大所需要的資源以及當前剩餘的可利用資源,可以計算可得知當前是否爲安全狀態(也就是一定不會發生死鎖),簡單來說就是確保當前可以利用的資源是充足的

四. 檢測死鎖

如果我們不去主動預防或者避免死鎖,那麼我們可以通過及時檢測當前是否出現死鎖的方式來處理死鎖問題,比如使用一些死鎖檢測算法

而什麼時候去進行死鎖檢測又取決於死鎖發生的頻率以及一般情況下死鎖涉及的進程數;這樣,我們既可以選擇定時檢測,也可以在發現資源利用率下降時進行檢測,總之,你得知道發生死鎖時,你的程序會有怎樣的變化。

監控工具 JConsole

JDK 也自帶了一個監控工具 JConsole,在JDK/bin目錄下可以找到。

五. 解除死鎖

當發生了死鎖之後,我們肯定要去解決掉它的。最直接的方法肯定是重啓,當然很多時候這樣做不太可取。

  • 終止相關進程:檢測出與死鎖有關的進程,撤銷或者掛起它,強制它釋放資源
  • 剝奪資源:將部分被死鎖進程佔用的資源剝奪出來,解除死鎖
  • 進程回退:將死鎖進程回退到未出問題之前,不過實現難度較大。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章