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