奇怪的死锁(?)

时间:2010-11-07 15:43:47

标签: java multithreading synchronization deadlock

我在使用两个线程的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 ,这给了我以下的线程转储:

  

“Thread-2”prio = 6   tid = 0x0000000006b09800 nid = 0x78fc   runnable [0x00000000083ef000]
  java.lang.Thread.State:RUNNABLE           at java.util.HashMap.put(Unknown Source)           在bgp.parser.Entry。 putToIdentifiedLinks (Entry.java:297)            - 已锁定&lt; 0x00000000853f2020&gt; (a bgp.parser.Entry)           在bgp.parser.Entry.parseTxtFile(Entry.java:141)           在bgp.parser.Entry.run(Entry.java:31)

     

“Thread-1”prio = 6   tid = 0x0000000006b52800 nid = 0x9390   runnable [0x00000000082ef000]
  java.lang.Thread.State:RUNNABLE           在java.util.HashMap.getEntry(未知   资源)           在java.util.HashMap.containsKey(未知   资源)           在bgp.parser.Entry。 identifiedLinksHasKey (Entry.java:281)            - 已锁定&lt; 0x00000000853f00e0&gt; (a bgp.parser.Entry)           在bgp.parser.Entry.parseTxtFile(Entry.java:134)           在bgp.parser.Entry.run(Entry.java:31)

它接缝两个线程同时访问两个同步函数,这与同步的含义相矛盾。即使我使用对象锁,也会发生相同的情况。虽然线程的状态不是BLOCKED但是RUNNABLE它们表现为阻塞,可能是因为它们同时访问相同的hashmap。

如果有人能解释我为什么会发生这种奇怪的情况,我真的很感激。

5 个答案:

答案 0 :(得分:7)

比较这两个:

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

他们持有不同的锁。 synchronized关键字锁定对象实例。 (即如果您创建了两个对象Object a=new Object(); Object b=new Object();,则锁定a不会影响b

答案 1 :(得分: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%确定是否可以使用类(Parser)而不是对象来锁定。如果这不起作用,只需选择可从两个线程/实例访问的任何(单个)对象。

答案 2 :(得分:1)

我怀疑identifiedLinksHasKey()putToIdentifiedLinks()方法正由bgp.parser.Entry类的两个不同实例执行,在这种情况下,synchronized将不起作用。

答案 3 :(得分:0)

如果Parser是单例类或Entry类的静态成员,则方法的同步将不起作用,因为它只保护Entry对象的成员变量。静态成员不受该计划的保护。最好的办法是,将Parser类的identifyLinks成员作为ConcurrentHashMap。

答案 4 :(得分:0)

您是否将所涉及的文件作为保证唯一身份的对象?如果您依赖它,比如说,您希望表示文件的对象在内存中保证全局唯一。它将一直有效,直到违反uniqness。

uniqness中的漏洞可能来自操作系统API。它以Windows而闻名(但很少有来自Unix的人很好理解),创建的文件的文件句柄与通过findFirst / findnext找到并打开的文件的文件句柄不同。

将操作系统中的文件API视为非常远程系统的通信API,不保证原因和后果。如果您创建文件,并不意味着您可以立即找到它,如果您删除文件,这可能意味着您之后可能仍会找到它。等。