2013-04-22 102 views
2

我有一個與Java線程活鎖有關的有趣問題。在這裏。Java線程鎖住

有四個全局鎖 - L1,L2,L3,L4

有四個線程 - T1,T2,T3,T4

T1需要鎖L1,L2,L3 T2需要鎖L2 T3需要的鎖L3,L4 T4需要鎖L1,L2

所以,問題的模式是 - 任何線程都可以運行並以任何順序獲取鎖。如果任何線程檢測到它所需的鎖不可用,它將釋放之前獲取的所有其他鎖,然後再次重試之前等待一段固定時間。循環重複導致活鎖定條件。

因此,要解決這個問題,我心裏有

1兩種解決方案),讓每一個線程等待重試之前的一段隨機時間。

OR, 

2)讓每個線程收購全部以特定的順序(即使線程不要求所有的 鎖)

我不相信這些是僅有的兩個選項提供給鎖我。請指教。

+0

確實。 (1)具有可避免的延遲並且(2)具有極端的死鎖潛力,正如Zim-Zam所強調的那樣,除非未能獲得鎖的線程釋放已經獲得並稍後重試的線程。 – 2013-04-22 15:41:41

回答

0

除非有很好的理由(性能明智)不這樣做, 我會統一所有鎖一把鎖對象。 這與您建議的解決方案2類似,只是在我看來更簡單。

順便說一句,這個解決方案不僅更簡單,而且bug更少, 性能可能會比您建議的解決方案1更好。

0

就我個人而言,我從未聽說過選項1,但我絕不是多線程專家。想到這件事後,聽起來好像可以正常工作。

然而,對付線程和資源鎖定的標準方法具有一定的相關選項2.爲了防止死鎖,資源需要總是以相同的順序進行收購。例如,如果您始終以相同的順序鎖定資源,則不會有任何問題。

0

用2a)讓每個線程以特定順序獲取它需要的所有鎖(不是所有的鎖)如果一個線程遇到鎖不可用,則它會釋放所有的鎖

只要線程獲得他們在你不能有死鎖相同的順序鎖;然而,你仍然可能會餓死(一個線程可能會遇到一個情況,它會一直釋放所有的鎖而不會前進)。爲了確保取得進展,可以爲線程分配優先級(0 =最低優先級,MAX_INT =最高優先級) - 在釋放鎖時增加線程的優先級,並在獲取所有鎖時將其減少到0。把你的等待線程放入一個隊列中,如果它需要與更高優先級的線程相同的資源,則不要啓動低優先級的線程 - 這樣可以保證優先級更高的線程最終獲得所有的鎖。不要實現這個線程隊列,除非你實際上遇到線程匱乏的問題,因爲它可能比只讓所有線程同時運行效率低。

您還可以通過實施omer schleifer的濃縮 - 所有鎖定解決方案來簡化操作;然而,除非你提到的四個線程之外的線程正在爭奪這些資源(在這種情況下,你仍然需要鎖定外部線程中的資源),你可以通過移除所有鎖並將線程在一個循環隊列中(所以你的線程只是按照相同的順序運行)。

1

當所有線程需要並釋放它們的一組鎖時,它們都進入單個受互斥鎖保護的狀態機。線程應該公開一些方法,它們返回它們需要繼續執行的一組鎖,併發出/等待一個私有信號量信號。 SM應該包含每個鎖的bool和一個'等待'隊列/數組/矢量/列表/任何容器來存儲等待線程。

如果線程進入SM互斥鎖以獲取鎖並可立即獲取其鎖定集,它可以重置其布爾集,退出互斥鎖並繼續。

如果一個線程進入SM互斥體並且無法立即獲得其鎖定集,它應該將自己添加到'等待'中,退出互斥體並等待其私有信號量。

如果一個線程進入SM互斥體釋放它的鎖,它會設置鎖布爾'返回'它的鎖並迭代'等待'以試圖找到一個線程,該線程現在可以使用一組可用的鎖來運行。如果它找到一個,它會正確地重置bools,從'Waiting'中刪除找到的線程併發出'找到'線程信號量的信號。然後它退出互斥體。

你可以用你用來匹配可用的set lock bools和你想要的等待線程的算法。也許你應該釋放需要最大匹配的線程,或者你想'旋轉''Waiting'容器元素以減少飢餓。由你決定。

像這樣的解決方案不需要輪詢(其性能會降低CPU的使用和延遲),也不需要連續獲取/釋放多個鎖。

用OO設計開發這樣的方案要容易得多。信號/等待信號量並返回所需的一組鎖的方法/成員函數通常可以填充到線程類繼承鏈中的某處。