阿里二面:如何快速排查死鎖?如何避免死鎖?

(文末有驚喜)

前言

相信程序員都會碰上這樣的問題,Java死鎖如何排查?如何解決呢?那麼,何爲死鎖呢?死鎖是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象。今天老顧一次性來幫助你們解決Java死鎖的有關問題。java

實例

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

接着咱們用代碼模擬上線的執行過程bash

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

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

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

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

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

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

$ jstack 96521複製代碼

從上面的堆棧信息中咱們能夠發現這個內容:「Found one Java-level deadlock」,表示程序中發現了一個死鎖,後面包含跟多詳細的信息,重點下面:性能

死鎖的代碼是在DeadLock.java的32行和18行,此時咱們就能夠去優化代碼,解決死鎖問題。測試

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

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

jconsole位於jdk的bin目錄中spa

$ jconsole複製代碼

能夠看到咱們的程序,點擊鏈接。

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

點擊「檢測死鎖」,能夠看到程序死鎖信息

上圖中能夠看到詳細的死鎖信息,和jstack中信息相似

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

VisualVM:jdk提供的一個很是強大的排查java程序問題的一個工具,能夠監控程序的性能、查看jvm配置信息、堆快照、線程堆棧信息。算是程序優化的必備工具。

工具位於jdk的bin目錄中。

$ jvisualvm複製代碼

切換到「線程」窗口

執行提示有死鎖狀況。在線程窗口中點擊「線程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方法,該方法能夠按照固定時長等待鎖,所以線程能夠在獲取鎖超時之後,主動釋放以前已經得到的全部的鎖。經過這種方式,也能夠頗有效地避免死鎖。

總結

死鎖就是「兩個任務以不合理的順序互相爭奪資源」形成,所以爲了規避死鎖,應用程序須要妥善處理資源獲取的順序。 另外有些時候,死鎖並不會立刻在應用程序中體現出來,在一般狀況下,都是應用在生產環境運行了一段時間後,纔開始慢慢顯現出來,在實際測試過程當中,因爲死鎖的隱蔽性,很難在測試過程當中及時發現死鎖的存在,並且在生產環境中應用出現了死鎖,每每都是在應用情況最糟糕的時候——在高負載狀況下。所以,開發者在開發過程當中要謹慎分析每一個系統資源的使用狀況,合理規避死鎖。

---End---

來來來,喜歡就給個小關注唄,掃一掃送java全新整套java資料哦


相關文章
相關標籤/搜索