Java併發程式設計實戰--通過執行緒轉儲資訊來分析死鎖

衣舞晨風發表於2017-06-03

      雖然防止死鎖的主要責任在於你自己,但JVM仍然通過執行緒轉儲來幫助識別死鎖的發生。執行緒轉儲包括各個執行中的執行緒的棧追蹤資訊,這類似於發生異常時的棧追蹤資訊。執行緒轉儲還包括加鎖資訊,例如每個執行緒持有了哪些鎖,在哪些棧幀中獲得這些鎖,以及被阻塞的執行緒正在等待獲取哪一個鎖。在生成執行緒轉儲之前,JVM將在等待關係圖通過迴圈來找出死鎖。如果發現了一個死鎖,則獲取相應的死鎖資訊,例如在死鎖中涉及哪些鎖和執行緒,以及這個鎖的獲取操作位於程式的哪些位置。

      要在UNIX平臺上觸發執行緒轉儲操作,可以通過向JVM的程式傳送SIGQUIT訊號(kill -3),或者在UNIX平臺中按下Ctrl-\鍵,在Windows平臺中按下Ctrl-Break鍵。在許多IDE(整合開發環境)中都可以請求執行緒轉儲。

     如果使用顯式的Lock類而不是內部鎖,那麼Java5.0並不支援與Lock相關的轉儲資訊,線上程轉儲中不會出現顯式地Lock。雖然在Java6中包含了對顯式Lock地執行緒轉儲和死鎖檢測等的支援,但在這些鎖上獲得的資訊比在內建鎖上獲得的資訊精確度低。內建鎖與獲得它們所在的執行緒棧幀是相關聯的,而顯式的Lock只與獲得它的執行緒相關聯。

      如下圖片給出了一個J2EE應用程式中獲取的部分執行緒的轉儲資訊。在導致死鎖的故障中包括3個元件:一個J2EE應用程式,一個J2EE容器,以及一個JDBC驅動程式,分別由不同的生產商提供。這3個元件都是商業產品,並經過大量的測試,但每一個元件中都存在一個錯誤,並且這個錯誤只有當它們進行互動時才會顯現出來,並導致伺服器出現一個嚴重的故障。
這裡寫圖片描述

     當診斷死鎖時,JVM可以幫我們做許多工作——哪些鎖導致了這個問題,設計哪些執行緒,它們持有哪些其他的鎖,以及是否間接地給其他執行緒帶來不利的影響。其中一個執行緒持有MumbleDBConnection上的鎖,並等待獲得MumbleDBCallableStatement上的鎖,而另一個執行緒持有MumbleDBCallableStatement上的鎖,並等待MumbleDDConnection上的鎖。

     在這裡使用的JDBC驅動程式中明視訊記憶體在一個鎖順序問題:不同的呼叫鏈通過JDBC驅動程式以不同的順序獲取多個鎖。如果不是由於另一個錯誤,這個問題永遠不會顯現出來:多個執行緒試圖同時使用同一個JDBC連線。這並不是應用程式的設計初衷——開發人員驚訝地發現同一個Connection被兩個執行緒併發使用。在JDBC規範中並沒有要求Connection必須是執行緒安全的,以及Connection通常被封閉在單個執行緒中使用,而在這裡就採用了這種假設。這個生產商檢視提供一個執行緒安全的JDBC驅動,因此在驅動程式程式碼內部對多個JDBC物件施加了同步機制。然而,生產商卻沒有考慮鎖的順序,因而驅動程式很容易發生死鎖,而正是由於這個存在死鎖風險的驅動程式與錯誤共享Connection的應用程式發生了互動,才使得這個問題暴露出來。因為單個錯誤並不會產生死鎖,只有這兩個錯誤同時發生時才會產生,即使它們分別進行了大量測試。

Java併發程式設計實戰pdf及案例原始碼下載:
http://download.csdn.net/detail/xunzaosiyecao/9851028

作者:jiankunking 出處:http://blog.csdn.net/jiankunking

相關文章