2010-11-07 96 views
1

我在使用兩個線程的Java應用程序中有一個非常奇怪的死鎖。兩個線程都將數據讀寫到共享的散列映射中。爲了避免同步問題我做了同步讀取和寫入數據HashMap中的功能:奇怪的死鎖(?)

private synchronized boolean identifiedLinksHasKey(String linkKey){ 
     return Parser.identifiedLinks.containsKey(linkKey); 
} 


private synchronized void putToIdentifiedLinks(String key, TreeSet<String> aset){  
     Parser.identifiedLinks.put(key,aset); 
} 

但是,程序掛在某些時候(當我與一個單獨的線程中運行它不發生)。要調試我的應用程序使用jstack它掛起後,這給了我下面的線程轉儲:

「線程2」 PRIO = 6 TID = 0x0000000006b09800 NID = 0x78fc 可運行[0x00000000083ef000]
的java .lang.Thread.State:RUNBUILD at java.util.HashMap.put(Unknown Source) at bgp.parser.Entry。 putToIdentifiedLinks(Entry.java:297) - 鎖定< 0x00000000853f2020>(一個bgp.parser.Entry) 在bgp.parser.Entry.parseTxtFile(Entry.java:141) 在bgp.parser.Entry.run( Entry.java:31)

「線程1」 PRIO = 6 TID = 0x0000000006b52800 NID = 0x9390 可運行的[0x00000000082ef000]
java.lang.Thread.State中:RUNNABLE 在java.util.HashMap.getEntry (Unknown Source) at java.util.HashMap.containsKey(Unknown Source) at bgp.parse r.Entry。 identifiedLinksHasKey(Entry.java:281) - 鎖定< 0x00000000853f00e0>在bgp.parser.Entry.run(一個bgp.parser.Entry) 在bgp.parser.Entry.parseTxtFile(Entry.java:134) ( Entry.java:31)

它接近兩個線程同時訪問兩個同步函數,這與同步的含義相矛盾。即使我使用對象鎖也會發生同樣的情況。儘管線程的狀態不是BLOCKED,而是RUNNABLE,但它們表現得像阻塞一樣,可能是因爲它們同時訪問相同的散列表。

我真的很感激,如果有人能解釋我爲什麼會出現這種奇怪的情況。

+3

這不是一個僵局。如果它看起來被阻塞了,那麼你手邊有一個不同的問題。 – 2010-11-07 16:17:20

+0

這是真的,我只是不知道該怎麼說。 – Vasilis 2010-11-07 22:08:00

回答

3

'synchronized'關鍵字鎖定在對象級別。即:兩個同步方法不能同時在一個對象內同時運行

是否有可能從兩個單獨的線程中調用兩個不同的對象?

編輯: 重新訪問堆棧跟蹤,我越來越有信心確實如此。更改代碼如下。

private boolean identifiedLinksHasKey(String linkKey){ 
     synchronized(Parser) { 
      return Parser.identifiedLinks.containsKey(linkKey); 
     } 
} 

private void putToIdentifiedLinks(String key, TreeSet<String> aset){  
    synchronized(Parser) {  
     Parser.identifiedLinks.put(key,aset); 
    } 
} 

我還沒有嘗試過這種代碼自己,我不是100%肯定它是否可以使用類(解析器),而不是一個對象鎖定。如果這不起作用,只需選擇可從兩個線程/實例訪問的任何(單個)對象。

+2

使用'Parser.class'(class level)或'Parser.identifiedLinks'(object level)來進行同步,後者在給定imho的上下文中是最好的。 – rsp 2010-11-07 17:13:19

+0

感謝您的回答。 rsp對它進行了細化,事實上Parser.identifiedLinks完成了這項工作。直到現在,我似乎還沒有對鎖的正確理解。 – Vasilis 2010-11-07 21:54:46

0

如果解析器是一個單例類或Entry類的靜態成員,那麼方法的同步將不起作用,因爲它只保護Entry對象的成員變量。靜態成員將不受該方案的保護。最好的辦法是,可能要使Parser類中的identifiedLinks成員成爲ConcurrentHashMap。

7

比較這兩個:

bgp.parser.Entry.putToIdentifiedLinks(Entry.java:297) - locked <0x00000000853f2020>
bgp.parser.Entry.identifiedLinksHasKey(Entry.java:281) - locked <0x00000000853f00e0>

他們都拿着不同的鎖。 ​​關鍵字鎖定對象實例。 (也就是說,如果你創建了兩個對象Object a=new Object();Object b=new Object();,鎖定在a不會影響b

+0

+1,這確實是我的錯誤。我剛剛接受了TumbleCow的解決方案,因爲它更詳細一些。 – Vasilis 2010-11-07 22:01:05

1

我懷疑identifiedLinksHasKey()putToIdentifiedLinks()方法正在被bgp.parser.Entry類的兩個不同的實例,執行在這種情況下​​將無法工作。

0

難道是你有涉及的文件作爲對象與保證唯一的身份?如果你依賴它,比如說,你期望代表文件的對象保證在內存中是全局唯一的。它會一直工作到uniqness被違反。

uniqness中的違規可能來自操作系統API。它以Windows而聞名(但很少很好地被來自Unix的人理解),所創建的文件的文件句柄不是通過findFirst/findnext找到並打開的文件的相同文件句柄。

將操作系統中的文件API看作是遠程系統的通信API,不能保證原因和結果。如果你創建文件,這並不意味着你可以立即找到它,如果你刪除了文件,這可能意味着你可能仍然會找到它。等等。