java併發編程(九)活躍性危險

線程的活躍性危險主要包括死鎖,飢餓和活鎖。

死鎖

  • 鎖順序死鎖:兩個線程試圖以不同的順序來獲得相同的鎖。
    比如一個線程先獲取a鎖後獲取b鎖,另一個線程先獲取b鎖後獲取a鎖。如果兩個按照相同的順序來請求鎖(在相同時間請求a鎖,在相同時間獲取b鎖),那麼沒問題,但是如果第一個線程獲取了a鎖的同時另一個線程獲取了b鎖,當兩個線程分別獲取第二個鎖時會發現各自需要的鎖都被對方持有了,也就發生了死鎖。
    爲了避免這種情況,可以使用**“加時賽”鎖**,在獲得兩個對象的鎖之前,首先獲得這個加時賽鎖,從而保證每次只有一個線程以未知的順序獲得這兩個鎖。
  • 在協作對象之間發生的死鎖
    如果在持有鎖的情況下調用某個外部方法,那麼就需要警惕在協作對象之間發生死鎖。
    如果在持有鎖時調用某個外部方法,那麼將出現活躍性問題,在這個外部方法中可能會獲得其他鎖(這可能會產生死鎖),或者阻塞時間過長,導致其他線程無法及時獲得當前被持有的鎖。
  • 資源死鎖
    線程飢餓死鎖。如果某些任務需要等待其他任務的結果,那麼這些任務往往是產生線程飢餓死鎖的主要來源。

飢餓

當線程由於無法訪問它所需要的資源而不能繼續執行時,就發生了“飢餓”。更嚴重的情況會發生飢餓死鎖。

在Thread API定義的線程優先級只是作爲線程調度的參考。在Thread API中定義了10個優先級,JVM根據需要將它們映射到操作系統的調度優先級,這種映射時與特定平臺(不同的操作系統)相關的。在某些操作系統中,如果優先級的數量少於10個,那麼有多個Java優先級會被映射到同一個優先級。

要避免使用線程優先級,因爲這會增加平臺依賴性,並可能導致活躍性問題。在大多數併發應用程序中,都可以使用默認的線程優先級。

活鎖

活鎖是另一種形式的活躍性問題,該問題不會導致線程阻塞,但也不能繼續執行。因爲線程將不斷重複執行相同的操作,而且總會失敗。
比如某個線程操作失敗後,放回隊列。下一次執行又失敗放回隊列無限循環。
活鎖通常時由過度的錯誤恢復代碼造成的,因爲它錯誤將不可修復的錯誤作爲可修復的錯誤。
典型想rocketMq的消息重試機制,它設定了一個閥值,當重試次數到達閥值之後會拋棄這個消息。如果沒有這個閥值,且消息一直消費錯誤,就產生了活鎖。

通過線程轉儲信息來分析線程

java的線程轉儲可以被定義爲JVM中在某一個給定的時刻運行的所有線程的快照。一個線程轉儲可能包含一個單獨的線程或者多個線程。在多線程環境中,比如J2EE應用服務器,將會有許多線程和線程組。每一個線程都有它自己的調用堆棧,在一個給定時刻,表現爲一個獨立功能。線程轉儲將會提供JVM中所有線程的堆棧信息,對於特定的線程也會給出更多信息。

通過線程轉儲可以查看線程的運行狀態,線程id,優先級等等一系列線程信息。

如何查看線程堆棧信息

  • 在linux服務器中 kill -3 pid (項目運行的jvm id)可以用ps aux|grep XXX命令查看
  • 在idea中可以直接在控制檯中查看。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章