傳說中的“死鎖”?西安一碼通奔潰,程式設計師竟無法搶修?

遞迴導致棧溢位!

12月20日早間,不少西安市民反映西安一碼通應用軟體出現故障無法開啟,頁面沒有個人資訊顯示,撥打西安一碼通服務熱線也無法接通,市民正常出行受阻,目前全市都無法使用。上午9時,官方表示由於一碼通軟體系統故障導致,系統目前正在努力搶修中,暫時無法確定修復完成時間,請大家耐心等待。

每當出現Bug,就到了考驗程式設計師的時刻。地鐵上、馬路邊、遊樂場……隨處可見程式設計師攻克Bug的身影,畢竟,每一個Bug,都難不倒所向披靡的戰士!但今天出現的一幕,也讓程式設計師犯了難!於是有了下圖:

傳說中的“死鎖”?西安一碼通奔潰,程式設計師竟無法搶修?

據報道,負責西安一碼通開發運維的是西安本土的企業,然而現在其工作人員也因為無法掃碼且沒有48小時核酸證明進不了公司,陷入了“沒有綠碼就不能上班,不上班就無法修復軟體的迴圈”。

傳說中的“死鎖”?西安一碼通奔潰,程式設計師竟無法搶修?

死鎖面試題(什麼是死鎖,產生死鎖的原因及必要條件)

什麼是死鎖?

所謂死鎖,是指多個程序在執行過程中因爭奪資源而造成的一種僵局,當程序處於這種僵持狀態時,若無外力作用,它們都將無法再向前推進。因此我們舉個例子來描述,如果此時有一個執行緒A,按照先鎖a再獲得鎖b的的順序獲得鎖,而在此同時又有另外一個執行緒B,按照先鎖b再鎖a的順序獲得鎖。如下圖所示:

產生死鎖的原因?

可歸結為如下兩點:

a。 競爭資源

系統中的資源可以分為兩類:

可剝奪資源,是指某程序在獲得這類資源後,該資源可以再被其他程序或系統剝奪,CPU和主存均屬於可剝奪性資源;

另一類資源是不可剝奪資源,當系統把這類資源分配給某程序後,再不能強行收回,只能在程序用完後自行釋放,如磁帶機、印表機等。

產生死鎖中的競爭資源之一指的是競爭不可剝奪資源(例如:系統中只有一臺印表機,可供程序P1使用,假定P1已佔用了印表機,若P2繼續要求印表機列印將阻塞)

產生死鎖中的競爭資源另外一種資源指的是競爭臨時資源(臨時資源包括硬體中斷、訊號、訊息、緩衝區內的訊息等),通常訊息通訊順序進行不當,則會產生死鎖。

b。 程序間推進順序非法

若P1保持了資源R1,P2保持了資源R2,系統處於不安全狀態,因為這兩個程序再向前推進,便可能發生死鎖例如,當P1執行到P1:Request(R2)時,將因R2已被P2佔用而阻塞;當P2執行到P2:Request(R1)時,也將因R1已被P1佔用而阻塞,於是發生程序死鎖。

死鎖產生的4個必要條件?

產生死鎖的必要條件:

互斥條件:程序要求對所分配的資源進行排它性控制,即在一段時間內某資源僅為一程序所佔用。

請求和保持條件:當程序因請求資源而阻塞時,對已獲得的資源保持不放。

不剝奪條件:程序已獲得的資源在未使用完之前,不能剝奪,只能在使用完時由自己釋放。

環路等待條件:在發生死鎖時,必然存在一個程序——資源的環形鏈。

解決死鎖的基本方法

預防死鎖:

資源一次性分配:一次性分配所有資源,這樣就不會再有請求了:(破壞請求條件)

只要有一個資源得不到分配,也不給這個程序分配其他的資源:(破壞請保持條件)

可剝奪資源:即當某程序獲得了部分資源,但得不到其它資源,則釋放已佔有的資源(破壞不可剝奪條件)

資源有序分配法:系統給每類資源賦予一個編號,每一個程序按編號遞增的順序請求資源,釋放則相反(破壞環路等待條件)

以確定的順序獲得鎖

如果必須獲取多個鎖,那麼在設計的時候需要充分考慮不同執行緒之前獲得鎖的順序。按照上面的例子,兩個執行緒獲得鎖的時序圖如下:

傳說中的“死鎖”?西安一碼通奔潰,程式設計師竟無法搶修?

如果此時把獲得鎖的時序改成:

傳說中的“死鎖”?西安一碼通奔潰,程式設計師竟無法搶修?

那麼死鎖就永遠不會發生。針對兩個特定的鎖,開發者可以嘗試按照鎖物件的hashCode值大小的順序,分別獲得兩個鎖,這樣鎖總是會以特定的順序獲得鎖,那麼死鎖也不會發生。問題變得更加複雜一些,如果此時有多個執行緒,都在競爭不同的鎖,簡單按照鎖物件的hashCode進行排序(單純按照hashCode順序排序會出現“環路等待”),可能就無法滿足要求了,這個時候開發者可以使用銀行家演算法,所有的鎖都按照特定的順序獲取,同樣可以防止死鎖的發生,該演算法在這裡就不再贅述了,有興趣的可以自行了解一下。

超時放棄

當使用synchronized關鍵詞提供的內建鎖時,只要執行緒沒有獲得鎖,那麼就會永遠等待下去,然而Lock介面提供了boolean tryLock(long time, TimeUnit unit) throws InterruptedException方法,該方法可以按照固定時長等待鎖,因此執行緒可以在獲取鎖超時以後,主動釋放之前已經獲得的所有的鎖。透過這種方式,也可以很有效地避免死鎖。還是按照之前的例子,時序圖如下:

傳說中的“死鎖”?西安一碼通奔潰,程式設計師竟無法搶修?

避免死鎖

預防死鎖的幾種策略,會嚴重地損害系統性能。因此在避免死鎖時,要施加較弱的限制,從而獲得 較滿意的系統性能。由於在避免死鎖的策略中,允許程序動態地申請資源。因而,系統在進行資源分配之前預先計算資源分配的安全性。若此次分配不會導致系統進入不安全的狀態,則將資源分配給程序;否則,程序等待。其中最具有代表性的避免死鎖演算法是銀行家演算法。

銀行家演算法:首先需要定義狀態和安全狀態的概念。系統的狀態是當前給程序分配的資源情況。因此,狀態包含兩個向量Resource(系統中每種資源的總量)和Available(未分配給程序的每種資源的總量)及兩個矩陣Claim(表示程序對資源的需求)和Allocation(表示當前分配給程序的資源)。安全狀態是指至少有一個資源分配序列不會導致死鎖。當程序請求一組資源時,假設同意該請求,從而改變了系統的狀態,然後確定其結果是否還處於安全狀態。如果是,同意這個請求;如果不是,阻塞該程序知道同意該請求後系統狀態仍然是安全的。

檢測死鎖

首先為每個程序和每個資源指定一個唯一的號碼;

然後建立資源分配表和程序等待表。

解除死鎖

當發現有程序死鎖後,便應立即把它從死鎖狀態中解脫出來,常採用的方法有:

剝奪資源:從其它程序剝奪足夠數量的資源給死鎖程序,以解除死鎖狀態;

撤消程序:可以直接撤消死鎖程序或撤消代價最小的程序,直至有足夠的資源可用,死鎖狀態。消除為止;所謂代價是指優先順序、執行代價、程序的重要性和價值等。

死鎖檢測

Jstack命令

jstack是java虛擬機器自帶的一種堆疊跟蹤工具。jstack用於打印出給定的java程序ID或core file或遠端除錯服務的Java堆疊資訊。Jstack工具可以用於生成java虛擬機器當前時刻的執行緒快照。執行緒快照是當前java虛擬機器內每一條執行緒正在執行的方法堆疊的集合,生成執行緒快照的主要目的是定位執行緒出現長時間停頓的原因,如執行緒間死鎖、死迴圈、請求外部資源導致的長時間等待等。執行緒出現停頓的時候透過jstack來檢視各個執行緒的呼叫堆疊,就可以知道沒有響應的執行緒到底在後臺做什麼事情,或者等待什麼資源。

JConsole工具

Jconsole是JDK自帶的監控工具,在JDK/bin目錄下可以找到。它用於連線正在執行的本地或者遠端的JVM,對執行在Java應用程式的資源消耗和效能進行監控,並畫出大量的圖表,提供強大的視覺化介面。而且本身佔用的伺服器記憶體很小,甚至可以說幾乎不消耗。

版權歸原作者所有,如有侵權,請聯絡刪除。

覺得不錯,點個“在看”然後轉發出去

TAG: 死鎖資源程序執行緒剝奪