我在使用两个线程的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。
如果有人能解释我为什么会发生这种奇怪的情况,我真的很感激。
答案 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,不保证原因和后果。如果您创建文件,并不意味着您可以立即找到它,如果您删除文件,这可能意味着您之后可能仍会找到它。等。