2010-11-07 99 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,不能保证原因和结果。如果你创建文件,这并不意味着你可以立即找到它,如果你删除了文件,这可能意味着你可能仍然会找到它。等等。