奇怪的死锁(?)
我在使用两个线程的 Java 应用程序中遇到了非常奇怪的死锁。两个线程都读取数据并将数据写入共享哈希图。为了避免同步问题,我同步了读取和写入哈希图数据的函数:
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”优先级=6 tid=0x0000000006b09800 nid=0x78fc 可运行[0x00000000083ef000]
java.lang.Thread.State:可运行 在 java.util.HashMap.put(来源未知) 在 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”优先级=6 tid=0x0000000006b52800 nid=0x9390 可运行[0x00000000082ef000]
java.lang.Thread.State:可运行 在java.util.HashMap.getEntry(未知 来源) 在 java.util.HashMap.containsKey(未知 来源) 在 bgp.parser.Entry.identifiedLinksHasKey(Entry.java:281) - 锁定<0x00000000853f00e0> (bgp.parser.Entry) 在 bgp.parser.Entry.parseTxtFile(Entry.java:134) 在 bgp.parser.Entry.run(Entry.java:31)
这表明两个线程同时访问两个同步函数,这与同步的含义相矛盾。即使我使用对象锁也会发生同样的情况。尽管线程的状态不是 BLOCKED 而是 RUNNABLE,但它们的行为是阻塞的,可能是因为它们同时访问相同的哈希图。
如果有人能向我解释为什么会发生这种奇怪的情况,我将非常感激。
I am having a very strange deadlock in a Java application that uses two threads. Both threads read and write data to a shared hashmap. To avoid synchronization problems I made synchronized the functions that read and write data the 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);
}
However, the program hangs at some point (which doesn't happen when I run it with a single thread). To debug my application I used jstack after it hangs, which gave me the following thread dump:
"Thread-2" prio=6
tid=0x0000000006b09800 nid=0x78fc
runnable [0x00000000083ef000 ]
java.lang.Thread.State: RUNNABLE
at java.util.HashMap.put(Unknown Source)
at bgp.parser.Entry.putToIdentifiedLinks(Entry.java:297)
- locked <0x00000000853f2020> (a bgp.parser.Entry)
at bgp.parser.Entry.parseTxtFile(Entry.java:141)
at bgp.parser.Entry.run(Entry.java:31)"Thread-1" prio=6
tid=0x0000000006b52800 nid=0x9390
runnable [0x00000000082ef000 ]
java.lang.Thread.State: RUNNABLE
at java.util.HashMap.getEntry(Unknown
Source)
at java.util.HashMap.containsKey(Unknown
Source)
at bgp.parser.Entry.identifiedLinksHasKey(Entry.java:281)
- locked <0x00000000853f00e0> (a bgp.parser.Entry)
at bgp.parser.Entry.parseTxtFile(Entry.java:134)
at bgp.parser.Entry.run(Entry.java:31)
It seams that the two threads access simultaneously the two synchronized functions which contradicts the meaning of synchronization. The same situation happens even if I use object locks. Although the threads' state is not BLOCKED but RUNNABLE they behave as blocked, probably because they access the same hashmap at the same time.
I would really appreciate if someone could explain me why this strange situation happens.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
比较这两个:
它们持有不同的锁。
synchronized
关键字锁定对象实例。 (即,如果创建两个对象Object a=new Object();
Object b=new Object();
,则锁定a
中的对象不影响b
)compare these two:
They are holding different lock.
The
synchronized
keyword lock over the object instance. (i.e. If you create two objectObject a=new Object();
Object b=new Object();
, locking the ina
won't affectb
)“synchronized”关键字在对象级别锁定。也就是说:在一个对象内不能同时运行两个同步方法。
是否有可能从两个单独的线程调用两个不同的对象?
编辑:
重新访问堆栈跟踪,我越来越确信情况确实如此。更改代码如下。
我自己没有尝试过这段代码,并且我不能 100% 确定是否可以使用类(解析器)而不是对象来锁定。如果这不起作用,只需选择可从两个线程/实例访问的任何(单个)对象。
The 'synchronized' keyword locks at object-level. That is: No two synchronized methods can be running at the same time within one object.
Is it possible that there are two distinct objects that are being called from the two separate threads?
edit:
re-visiting the stack trace, I'm getting increasingly confident that this is indeed the case. Change the code as following.
I haven't tried this code myself, and I'm not 100% sure whether it's possible to use a class (Parser) rather than an object to lock on. If this doesn't work, just pick any (single) object that's accessible from both threads / instances.
我怀疑
identifiedLinksHasKey()
和putToIdentifiedLinks()
方法是由bgp.parser.Entry
类的两个不同实例执行的,在这种情况下,synchronized
将不起作用。I suspect that the
identifiedLinksHasKey()
and theputToIdentifiedLinks()
methods are being executed by two different instances of thebgp.parser.Entry
class, in which case thesynchronized
won't work.如果Parser是单例类或者Entry类的静态成员,那么方法的同步将不起作用,因为它只保护Entry对象的成员变量。静态成员将不受该方案的保护。最好的选择是,可能使 Parser 类的identifiedLinks 成员成为ConcurrentHashMap。
If Parser is a singleton class or a static member of the Entry class, then the synchronization of the methods will not work as it only protects member variables of the Entry object. Static members will not be protected by that scheme. Your best bet is, probably to make the identifiedLinks member of the Parser class be a ConcurrentHashMap.
是否您已将文件作为具有保证唯一标识的对象?如果您依赖它,比如说,您希望代表文件的对象在内存中保证是全局唯一的。它将一直有效,直到唯一性被侵犯为止。
唯一性的破坏可能来自操作系统 API。对于 Windows 来说众所周知(但来自 Unix 的人们很少能很好地理解),创建的文件的文件句柄与刚刚通过 findFirst/findnext 找到并打开的文件的文件句柄不同。
将操作系统中的文件 API 视为与非常远程系统的通信 API,不保证因果关系。如果您创建文件,并不意味着您可以立即找到它,如果您删除文件,则可能意味着您以后仍然可能找到它。 ETC。
Could it be that you have involved files as objects with guaranteed unique identity ? If you rely on it, say, you expect that object, representing file is guaranteed to be globally unique in memory. It will work until uniqness will be violated.
The breach in uniqness can come from operating system API. It is known for Windows (but rarely is well understood by people coming from Unix for example), that file handle of file created is not the same file handle of file just being found and opened through findFirst/findnext.
Consider file API in operating system as just a communication API to very remote system with no guarantee of cause and consequence. If you create file, it does not mean you can immediatelly find it, if you delete file, it may mean you will possibly still find it afterwards. etc.