面試官:如何快速排查死鎖?如何避免死鎖?

前言

相信程序員都會碰上這樣的問題,Java死鎖如何排查?又如何解決呢?那麼,何為死鎖呢?死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象。今天一次性來幫助大家解決Java死鎖的有關問題。

實例

死鎖的本質,舉個例子如果此時有一個線程 A ,按照先獲持有鎖 a 再獲取鎖 b的順序獲得鎖,同時另外一個線程 B,按照先獲取鎖 b 再獲取鎖 a 的順序獲取鎖。如下圖所示:

面試官:如何快速排查死鎖?如何避免死鎖?

接著我們用代碼模擬上線的執行過程

面試官:如何快速排查死鎖?如何避免死鎖?

直接運行,發現主線程一直處於執行中,一直無法結束

面試官:如何快速排查死鎖?如何避免死鎖?

通過jdk工具jps、jstack排查死鎖問題

步驟一:使用jsp查找程序進行

jps:jdk提供的一個工具,可以查看到正在運行的java進程

面試官:如何快速排查死鎖?如何避免死鎖?

步驟二:使用jstack查看線程堆棧信息

jstack:jdk提供的一個工具,可以查看java進程中線程堆棧信息。更詳細的用法見文檔最後。

<code>$ jstack 96521複製代碼/<code>
面試官:如何快速排查死鎖?如何避免死鎖?

從上面的堆棧信息中我們可以發現這個內容:“Found one Java-level deadlock”,表示程序中發現了一個死鎖,後面包含更多詳細的信息,重點下面:

面試官:如何快速排查死鎖?如何避免死鎖?

死鎖的代碼是在DeadLock.java的32行和18行,此時我們就可以去優化代碼,解決死鎖問題。

通過jdk提供的工具jconsole排查死鎖問題

jconsole:jdk提供的一個可視化的工具,方便排查程序的一些問題,如:程序內存溢出、死鎖問題等等。更詳細的用法見文檔最後。jconsole位於jdk的bin目錄中

<code>$ jconsole複製代碼/<code>
面試官:如何快速排查死鎖?如何避免死鎖?

可以看到我們的程序,點擊連接。

在jconsole窗口中查看線程堆棧信息

點擊“檢測死鎖”,可以看到程序死鎖信息

面試官:如何快速排查死鎖?如何避免死鎖?

面試官:如何快速排查死鎖?如何避免死鎖?

上圖中可以看到詳細的死鎖信息,和jstack中信息類似。

通過jdk提供的工具VisualVM排查死鎖問題

VisualVM:jdk提供的一個非常強大的排查java程序問題的一個工具,可以監控程序的性能、查看jvm配置信息、堆快照、線程堆棧信息。算是程序優化的必備工具。工具位於jdk的bin目錄中。

<code>$ jvisualvm複製代碼/<code> 
面試官:如何快速排查死鎖?如何避免死鎖?

切換到“線程”窗口

面試官:如何快速排查死鎖?如何避免死鎖?

執行提示有死鎖情況。在線程窗口中點擊“線程Dump”按鈕。

查看堆棧信息

面試官:如何快速排查死鎖?如何避免死鎖?

線程堆棧快照的信息和jstack查看到的信息一樣,即可發現死鎖代碼。

如何避免死鎖?

我們知道了死鎖如何產生的

,那麼就知道該如何去預防。如果一個線程每次只能獲取一個鎖,那麼就不會出現由於嵌套持有鎖順序導致的死鎖

1. 正確的順序獲得鎖

如果必須獲取多個鎖,我們就要考慮不同線程獲取鎖的順序。

上面的例子出現死鎖的根本原因就是獲取所的順序是亂序的,超乎我們控制的。上面例子最理想的情況就是把業務邏輯抽離出來,把獲取鎖的代碼放在一個公共的方法裡面,讓這兩個線程獲取鎖都是從我的公共的方法裡面獲取。

當Thread1線程進入公共方法時,獲取了A鎖,另外Thread2又進來了,但是A鎖已經被Thread1線程獲取了,所以只能阻塞等待。Thread1接著又獲取鎖B,Thread2線程就不能再獲取不到了鎖A,更別說再去獲取鎖B了,這樣就有一定的順序了。只有當線程1釋放了所有鎖,線程B才能獲取。

比如前面的例子我們改成

面試官:如何快速排查死鎖?如何避免死鎖?

查看打印結果,我們發現 線程0 獲取成功然後線程1才能繼續獲取

面試官:如何快速排查死鎖?如何避免死鎖?

2. 超時放棄

當線程獲取鎖超時了則放棄,這樣就避免了出現死鎖獲取的情況。當使用synchronized關鍵詞提供的內置鎖時,只要線程沒有獲得鎖,那麼就會永遠等待下去,然而Lock接口提供了boolean tryLock(long time, TimeUnit unit) throws InterruptedException方法,該方法可以按照固定時長等待鎖,

因此線程可以在獲取鎖超時以後,主動釋放之前已經獲得的所有的鎖。通過這種方式,也可以很有效地避免死鎖。

總結

死鎖就是“兩個任務以不合理的順序互相爭奪資源”造成,因此為了規避死鎖,應用程序需要妥善處理資源獲取的順序。另外有些時候,死鎖並不會馬上在應用程序中體現出來,在通常情況下,都是應用在生產環境運行了一段時間後,才開始慢慢顯現出來,在實際測試過程中,由於死鎖的隱蔽性,很難在測試過程中及時發現死鎖的存在,而且在生產環境中應用出現了死鎖,往往都是在應用狀況最糟糕的時候——在高負載情況下。因此,開發者在開發過程中要謹慎分析每個系統資源的使用情況,合理規避死鎖。


分享到:


相關文章: