? ? 假設有一個共享資源庫 ResourcePool
,它內部維護了兩類資源:ResourceTypeA
和 ResourceTypeB
。現在有兩個線程 Thread1
和 Thread2
,它們都需要從資源庫中分別獲取一種資源才能繼續執行。Thread1
需要 ResourceTypeA
而 Thread2
需要 ResourceTypeB
。資源庫提供了等待和通知機制,允許線程在資源不可用時等待,并在資源變為可用時得到通知。
初始條件
ResourcePool
?中?ResourceTypeA
?和?ResourceTypeB
?都不可用。Thread1
?首先到達,開始等待?ResourceTypeA
。Thread2
?隨后到達,開始等待?ResourceTypeB
。
死鎖發生過程
-
資源釋放與等待
ResourceTypeA
?變為可用,此時?ResourcePool
?應該通知等待的線程。ResourcePool
?使用?notify()
?方法嘗試喚醒一個等待的線程。
-
錯誤的線程被喚醒
- 假設?
notify()
?方法隨機喚醒了?Thread2
?而不是?Thread1
。 Thread2
?嘗試獲取?ResourceTypeB
,但是它仍然不可用,因此?Thread2
?無法繼續執行,它再次進入等待狀態。
- 假設?
-
正確的線程未被喚醒
Thread1
,它實際上可以使用現在可用的?ResourceTypeA
,卻沒有被喚醒,因此它繼續等待。
-
沒有更多的通知
- 由于?
ResourcePool
?只調用了一次?notify()
,并且沒有新的資源變為可用來觸發額外的通知,Thread1
?將永遠等待下去,即使它需要的資源已經可用。
- 由于?
-
死鎖發生
Thread1
?和?Thread2
?都在等待,無法繼續執行,也無法喚醒對方,系統進入死鎖狀態。
死鎖的解決
- 使用?
notifyAll()
?替代?notify()
,這樣在?ResourceTypeA
?變為可用時,所有等待的線程都會被喚醒。Thread1
?和?Thread2
?都有機會檢查它們等待的資源是否可用,并相應地繼續執行或者繼續等待。 - 設計資源獲取邏輯時使用循環來檢查條件,并在條件不滿足時繼續等待(超時等待)。這樣即使被錯誤喚醒,線程也會再次檢查資源是否滿足其需求,并在不滿足時重新等待,而不是假設資源已經可用并進入死鎖狀態。
? ?通過這種方式,可以確保每個線程在其所需資源變為可用時能夠被喚醒并繼續執行,從而避免了死鎖的發生。