Java世界中類加載時出現的隱性死鎖

在今年遭遇過一次執行了某個操作後,代碼中某一個執行流就是不能執行下去的故障現象......,像極了線程死鎖!

好在,知道Java提供了些工具可以觀察JVM中所有線程的快照,例如在Linux使用kill -3 pid讓JVM輸出各線程的堆棧快照,可以去分析、分析是否有死鎖?

但,初步分析結果是令人驚訝的!

Java程序輸出的dump信息顯示沒有明顯的死鎖,但是在事後來看,事後諸葛亮一下,日誌也是給出了一些蛛絲馬跡。

dump信息中顯示其中有一個特殊線程,雖然線程狀態處於runnable狀態,但是線程執行流dump日誌中有一個細節的特殊指明--Object.wait()狀態。

當時一直疑惑,線程狀態處於runnable狀態就應該執行流繼續執行,但是,在實際故障現象中就是這個執行流無法執行下去!苦惱了很久。。。

後來詳細分析各線程堆棧快照,以及查看Object.wait所需要等待的物事,發現此無法繼續執行線程等待是一個在另外一個執行流中處於類加載的Class。進一步深入分析dump信息,又顯示此Class類加載時竟然要獲取一個業務級別的Lock,而這個Lock在執行不下去執行流,已在使用這個類前,已先行掌握到!

雖然在這個場景下,沒有出現典型的死鎖--多於兩個執行流的線程在申請鎖的順序上存在不一致,這樣就會在多線程競爭情況下就容易出現死鎖。但考慮到類加載,JVM會保證Class只會被加載一次且保證線程安全,推想來看這裏面肯定是存在一個隱含的鎖機制。

因爲類加載作爲一種含有特殊的隱含鎖過程,執行流必須等待類加載完成,如果此類加載過程中,又去獲取其它執行流可能先期佔用的業務級別上的鎖,則就容易在此類加載時出現類似隱性死鎖現象。JAVA世界中又一死鎖的可能場景!

後來解決起來也很簡單,類加載時申請業務級別的鎖,這種代碼實現在我看來就是一種設計瑕疵,在不必要底層機制過程中反調、回調屬於代碼運行時中高層資源,存在設計上回路,應該避免在程序金字塔中出現飛線!

 

#附錄 線程執行流

Thread1                               Thread2

hold Xlock                           ...

load Yclass                         load Yclass

       Object.wait                     Yclass.init #類加載已展開

                                                apply for Xlock

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章