多个Java线程似乎锁定了同一个监视器?

时间:2012-03-02 16:39:36

标签: java multithreading concurrency locking

在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)

这里的重点是两个线程都锁定了监视器&lt; 0x0000000682f99d98&gt; (无论它们现在正在等待两个不同的其他监视器)。

在查看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)。

Can several threads hold a lock on the same monitor in Java?中已经提出了类似的问题,但对我来说,答案并没有看到多个线程锁定同一个监视器的真正意义,即使他们可能正在等待其他一些监视器。

2014年5月13日更新:

较新的问题Multiple threads hold the same lock?包含重现行为的代码,而@rsxg已根据其答案提交了相应的错误报告https://bugs.openjdk.java.net/browse/JDK-8036823

4 个答案:

答案 0 :(得分:3)

我不认为你的线程转储是说你的两个线程是“等待两个不同的其他监视器”。我想这是说他们都在同一台显示器上等待,但是在两个不同的代码点。这可能是堆栈位置或对象实例位置等。这是一篇关于analyzing the stack dumps的精彩文档。

  

几个线程可以在Java中对同一个监视器进行锁定吗?

没有。您的堆栈转储显示在同一代码位置但在不同堆栈帧中锁定在同一监视器上的两个线程 - 或者看起来与操作系统相关的任何值。

修改

我不确定为什么线程转储似乎在说两个线程都有一个线路锁定,因为只有当它们在wait()方法中时才允许这样做。我注意到你链接到1.6.5版。这真的是你正在使用的版本吗?在版本2.3.6(可能是最新版本)中,1725 line实际上是wait

1722        synchronized (this) {
1723            while (currentlyLoading.contains(id)) {
1724                try {
1725                    wait();
1726                } catch (InterruptedException e) {

即使它是一个独占的synchronized锁,你也可以看到这种堆栈跟踪。例如,Linux下面的以下堆栈转储用于从同一代码行锁定在同一对象上的两个线程,但是在Runnable.run()方法的两个不同实例中。这是我的stupid little test program。请注意,监视器条目号是不同的,即使它是相同的锁和相同的代码行号。

"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)

This Oracle document将该值描述如下:

  

地址范围,它给出了线程的有效堆栈区域的估计值

答案 1 :(得分:2)

当一个线程锁定一个对象但是wait()时,另一个线程可以锁定同一个对象。您应该能够看到许多线程“持有”相同的锁等待。

AFAIK,唯一的另一种情况是当多个线程已锁定并等待并准备重新获取锁定时,例如在notifyAll()上。他们不再等待,但在他们再次获得锁定之前无法继续。 (一次只有一个线程可以做到这一点)

答案 2 :(得分:2)

在分析严重争用的锁时,您可能会遇到JVM中堆栈跟踪例程中的修饰错误 - 它可能与this bug相同或不同。

事实是,你的两个线程都没有真正设法获得SharedItemStateManager上的锁,正如你可以从他们报告waiting for monitor entry的事实中看到的那样。错误是在两种情况下,堆栈跟踪中的进一步向上报告waiting to lock而不是locked

分析这样的奇怪堆栈跟踪时的解决方法是始终检查声称拥有locked对象的线程是否也在等待获取同一对象的锁定。

(不幸的是,这种分析需要交叉引用堆栈跟踪中的行号与源代码,因为waiting for monitor entry标题中的数字与堆栈中的locked行之间没有任何关系根据{{​​3}},行0x00007f2a21278000中的数字TP-Processor184" daemon prio=10 tid=0x00007f2a7c056800 nid=0x47e7 waiting for monitor entry [0x00007f2a21278000]指的是对线程的有效堆栈区域的估计。所以它看起来像一个监视器ID,但它不是 - 你可以看到你给出的两个线程在堆栈中的不同地址。)

答案 3 :(得分:0)

"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)

在我们的线程转储中,我们有几个线程锁定相同的监视器,但只有一个线程是 runnable 。可能是因为锁定竞争,我们有284个其他线程在等待锁定。 Multiple threads hold the same lock?表示这只存在于线程转储中,因为线程转储不是原子操作。