[英]Multiple Java threads seemingly locking same monitor?
在Java threaddump中,我发现了以下内容:
"TP-Processor184" daemon prio=10 tid=0x00007f2a7c056800 nid=0x47e7 waiting for monitor entry [0x00007f2a21278000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.apache.jackrabbit.core.state.SharedItemStateManager.getNonVirtualItemState(SharedItemStateManager.java:1725)
- locked <0x0000000682f99d98> (a org.apache.jackrabbit.core.state.SharedItemStateManager)
at org.apache.jackrabbit.core.state.SharedItemStateManager.getItemState(SharedItemStateManager.java:257)
"TP-Processor137" daemon prio=10 tid=0x00007f2a7c00f800 nid=0x4131 waiting for monitor entry [0x00007f2a1ace7000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.apache.jackrabbit.core.state.SharedItemStateManager.getNonVirtualItemState(SharedItemStateManager.java:1725)
- locked <0x0000000682f99d98> (a org.apache.jackrabbit.core.state.SharedItemStateManager)
at org.apache.jackrabbit.core.state.SharedItemStateManager.getItemState(SharedItemStateManager.java:257)
这里的重点是两个线程都锁定了监视器<0x0000000682f99d98> (无论它们现在正在等待两个不同的其他监视器)。
在查看Thread Dump Analyzer时,如果选择了该监视器,它实际上会在底部显示“Threads locking monitor:2”,并且“2 Threads s slock”。 有关屏幕截图,请参阅https://lh4.googleusercontent.com/-fCmlnohVqE0/T1D5lcPerZI/AAAAAAAAD2c/vAHcDiGOoMo/s971/locked_by_two_threads_3.png ,我不允许在此处粘贴图片。
这是否意味着关于监视器锁定信息,threaddumps不是原子的? 我无法想象这真的是JVM的锁定错误(1.6.0_26-b03)。
类似的问题已经在几个线程中被问到可以锁定Java中的同一个监视器吗? ,但我的答案并没有看到多个线程锁定同一个监视器的真正意义,即使他们可能正在等待其他一些。
2014年5月13日更新:
更新的问题多个线程持有相同的锁? 有代码重现行为,@ rssx已根据他的答案提交了一份相应的错误报告https://bugs.openjdk.java.net/browse/JDK-8036823 。
我不认为你的线程转储说你的两个线程是“等待两个不同的其他监视器”。 我想这是说他们都在同一台显示器上等待,但是在两个不同的代码点。 这可能是堆栈位置或对象实例位置等。 这是一个关于分析堆栈转储的好文档。
几个线程可以在Java中对同一个监视器进行锁定吗?
不会。您的堆栈转储显示在相同代码位置但在不同堆栈帧中锁定在同一监视器上的两个线程 - 或者看起来与操作系统相关的任何值。
编辑:
我不确定为什么线程转储似乎是说两个线程都有一个行被锁定,因为这似乎只有在它们处于wait()
方法时才被允许。 我注意到你链接到1.6.5版。 这真的是你正在使用的版本吗? 在版本2.3.6(可能是最新版本)中, 1725行实际上是一个wait
。
1722 synchronized (this) {
1723 while (currentlyLoading.contains(id)) {
1724 try {
1725 wait();
1726 } catch (InterruptedException e) {
即使它是一个独占的synchronized
锁,你也可以看到这种堆栈跟踪。 例如,Linux下的以下堆栈转储用于从同一代码行锁定在同一对象上的两个线程,但是在Runnable.run()
方法的两个不同实例中。 这是我愚蠢的小测试程序 。 请注意,监视器条目号是不同的,即使它是相同的锁和相同的代码行号。
"Thread-1" prio=10 tid=0x00002aab34055c00 nid=0x4874
waiting for monitor entry [0x0000000041017000..0x0000000041017d90]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00002aab072a1318> (a java.lang.Object)
at com.mprew.be.service.auto.freecause.Foo$OurRunnable.run(Foo.java:38)
- locked <0x00002aab072a1318> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:619)
"Thread-0" prio=10 tid=0x00002aab34054c00 nid=0x4873
waiting for monitor entry [0x0000000040f16000..0x0000000040f16d10]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00002aab072a1318> (a java.lang.Object)
at com.mprew.be.service.auto.freecause.Foo$OurRunnable.run(Foo.java:38)
- locked <0x00002aab072a1318> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:619)
在我的Mac上,格式不同,但“监视器条目”之后的数字对于相同的行号不同。
"Thread-2" prio=5 tid=7f8b9c00d000 nid=0x109622000
waiting for monitor entry [109621000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <7f3192fb0> (a java.lang.Object)
at com.mprew.be.service.auto.freecause.Foo$OurRunnable.run(Foo.java:38)
- locked <7f3192fb0> (a java.lang.Object)
"Thread-1" prio=5 tid=7f8b9f80d800 nid=0x10951f000
waiting for monitor entry [10951e000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <7f3192fb0> (a java.lang.Object)
at com.mprew.be.service.auto.freecause.Foo$OurRunnable.run(Foo.java:38)
- locked <7f3192fb0> (a java.lang.Object)
此Oracle文档将该值描述如下:
地址范围,它给出了线程有效堆栈区域的估计值
在分析大量竞争锁时,您可能会遇到JVM中堆栈跟踪例程中的修饰错误 - 它可能与此错误相同或不同。
事实是,你的两个线程都没有实际设法获得SharedItemStateManager
上的SharedItemStateManager
,正如你可以看到它们正在报告waiting for monitor entry
的事实。 错误是在两种情况下进一步向上堆栈跟踪它们应该报告waiting to lock
而不是locked
。
分析这样的奇怪堆栈跟踪时的解决方法是始终检查声称已locked
对象的线程是否也在等待获取对同一对象的锁定。
(不幸的是,这种分析需要交叉引用堆栈跟踪中的行号与源代码,因为waiting for monitor entry
头中的数字与堆栈跟踪中的locked
行之间没有关系。根据此Oracle文档 ,行TP-Processor184" daemon prio=10 tid=0x00007f2a7c056800 nid=0x47e7 waiting for monitor entry [0x00007f2a21278000]
的数字0x00007f2a21278000
TP-Processor184" daemon prio=10 tid=0x00007f2a7c056800 nid=0x47e7 waiting for monitor entry [0x00007f2a21278000]
是指线程的有效堆栈区域的估计 。所以它看起来像一个监视器ID,但它不是' - 你可以看到你给出的两个线程在堆栈中的不同地址)。
当一个线程锁定一个对象但是wait()时,另一个线程可以锁定同一个对象。 您应该能够看到许多线程“持有”相同的锁等待。
AFAIK,唯一的另一种情况是当多个线程已经锁定并等待并准备重新获取锁定时,例如在notifyAll()上。 他们不再等待,但在他们再次获得锁定之前无法继续。 (一次只有一个线程可以做到这一点)
"http-0.0.0.0-8080-96" daemon prio=10 tid=0x00002abc000a8800 nid=0x3bc4 waiting for monitor entry [0x0000000050823000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.apache.lucene.search.FieldCacheImpl$Cache.get(FieldCacheImpl.java:195)
- locked <0x00002aadae12c048> (a java.util.WeakHashMap)
"http-0.0.0.0-8080-289" daemon prio=10 tid=0x00002abc00376800 nid=0x2688 waiting for monitor entry [0x000000005c8e3000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.apache.lucene.search.FieldCacheImpl$Cache.get(FieldCacheImpl.java:195)
- locked <0x00002aadae12c048> (a java.util.WeakHashMap
"http-0.0.0.0-8080-295" daemon prio=10 tid=0x00002abc00382800 nid=0x268e runnable [0x000000005cee9000]
java.lang.Thread.State: RUNNABLE
at org.apache.lucene.search.FieldCacheImpl$Cache.get(FieldCacheImpl.java:195)
- locked <0x00002aadae12c048> (a java.util.WeakHashMap)
在我们的线程转储中,我们有几个线程锁定相同的监视器,但只有一个线程可以运行 。 可能是因为锁定竞争 ,我们有284个其他线程在等待锁定。 多个线程持有相同的锁? 说这只存在于线程转储中,因为线程转储不是原子操作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.