Несколько потоков Java, похоже, блокируют один и тот же монитор?

В 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 > (независимо от того, что они ждут сейчас еще двух других мониторов).

При просмотре анализатора потоков Dump, при выборе этого монитора, он действительно говорит "Монитор блокировки потоков: 2" внизу и "2 Thread (s) lock". Пожалуйста, смотрите https://lh4.googleusercontent.com/-fCmlnohVqE0/T1D5lcPerZI/AAAAAAAAD2c/vAHcDiGOoMo/s971/locked_by_two_threads_3.png для скриншота, мне не разрешено вставлять изображения здесь.

Означает ли это, что threaddumps не являются атомарными относительно информации блокировки монитора? Я не могу себе представить, что это действительно ошибка блокировки JVM (1.6.0_26-b03).

Подобный вопрос уже задан в Может ли несколько потоков удерживать блокировку на одном мониторе в Java?, но ответ на мой вопрос не видел реальной точки нескольких потоков, блокирующих один и тот же монитор, даже если они могут ждать другого.

Обновление 13 мая 2014 года:

Новый вопрос Несколько потоков имеют одну и ту же блокировку? имеет код для воспроизведения поведения, а @rsxg подал соответствующий отчет об ошибке https://bugs.openjdk.java.net/browse/JDK-8036823 в соответствии с его ответом здесь.

Ответ 1

Вероятно, вы сталкиваетесь с косметической ошибкой в ​​процедурах отслеживания стека в JVM при анализе жестко заблокированных блокировок - это может быть или не быть таким же, как this ошибка.

Дело в том, что ни один из ваших двух потоков не смог получить блокировку на SharedItemStateManager, как вы можете видеть из того, что они сообщают waiting for monitor entry. Ошибка в том, что дальше в трассировке стека в обоих случаях они должны сообщать waiting to lock вместо locked.

Обходной путь при анализе странных трассировок стека, как это, должен всегда проверять, что поток, требующий иметь locked, объект также не ждет, чтобы получить блокировку на одном и том же объекте.

(К сожалению, для этого анализа требуется перекрестная ссылка номеров строк в трассировке стека с кодом источника, поскольку нет связи между цифрами в заголовке waiting for monitor entry и строкой locked в трассировке стека. этот документ Oracle, номер 0x00007f2a21278000 в строке TP-Processor184" daemon prio=10 tid=0x00007f2a7c056800 nid=0x47e7 waiting for monitor entry [0x00007f2a21278000] относится к оценке допустимой области стека для потока. как и идентификатор монитора, но это не так, и вы можете видеть, что два потока, которые вы указали, имеют разные адреса в стеке).

Ответ 2

Я не думаю, что ваш дамп потока говорит, что ваши два потока "ждут двух других мониторов". Я думаю, что он говорит, что они оба ждут на одном мониторе, но в двух разных кодовых точках. Это может быть местоположение стека или местоположение экземпляра объекта или что-то еще. Это отличный документ о анализе дампов стека.

Может ли несколько потоков удерживать блокировку на одном мониторе в Java?

Нет. Дамп стека показывает два потока, заблокированных на одном мониторе в одном и том же кодовом местоположении, но в разных кадрах стека - или независимо от того, что это значение зависит от ОС.

Edit:

Я не уверен, почему дамп потока, похоже, говорит о том, что оба потока имеют линию, поскольку это кажется допустимым только в том случае, если они находятся в методе 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(). Здесь моя глупая небольшая тестовая программа. Обратите внимание, что номера записей в мониторе отличаются друг от друга, даже считаются одной и той же блокировкой и одинаковым номером строки кода.

"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 описывает это значение следующим образом:

Диапазон адресов, который дает оценку действительной области стека для потока

Ответ 3

Когда поток блокирует объект, но wait(), другой поток может заблокировать один и тот же объект. Вы должны уметь видеть, что ряд потоков "удерживает" одну и ту же блокировку, все ждущие.

AFAIK, единственный случай - когда несколько потоков заблокированы и ждут и готовы повторно захватить блокировку, например. на notifyAll(). Они больше не ждут, но не могут продолжить, пока они не вернут замок. (это может сделать только один поток за раз)

Ответ 4

"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 других потока, ожидающих блокировки. Несколько потоков содержат одну и ту же блокировку? сказал, что это существует только в дампе потока, поскольку дамп потока не является атомной операцией.