thread dump что это
Многопоточность в Java. Лекция 6: взаимные блокировки и дампы потоков
В пятой лекции краткого курса о многопоточности речь шла об атомарных переменных и многопоточных коллекциях. На этот раз наши коллеги рассказывают о дампах потоков, простых и скрытых дедлоках.
В некорректно спроектированной многопоточной программе может возникнуть ситуация, когда два потока блокируют друг друга. В этом случае их выполнение зависает, пока программу не остановят извне. Такая ситуация называется deadlock.
6.1 Дампы потоков
Помимо дедлоков, бывает, что поток очень долго ожидает какие-то ресурсы или остается постоянно активным, например, выполняя очень большой или бесконечный цикл. Для выявления таких ситуаций и других проблем, связанных с потоками, JVM предоставляет возможность сделать мгновенный снимок состояния потоков. Такой снимок называется thread dump. Он представляет собой текстовый документ, в котором перечислены все потоки, в том числе, потоки JVM. Для каждого потока отображается стандартный набор информации: имя, статус, приоритет, стек-трейс, демон ли поток или нет, а также адрес объекта блокировки, на которой находится поток. Часть такого thread dump приведена в листинге 1.
Листинг 1
«Java Thread» #11 prio=5 os_prio=0 tid=0x00007fb0a4356000 nid=0x1242 waiting for monitor entry [0x00007fb078701000] java.lang.Thread.State: BLOCKED (on object monitor)
at com.da.lect5.deadlock.TwoTasks.lambda$getTask1$0(TwoTasks.java:14)
— waiting to lock (a java.lang.String)
— locked (a java.lang.String)
at com.da.lect5.deadlock.TwoTasks$$Lambda$1/1078694789.run(Unknown
Source)
at java.lang.Thread.run(Thread.java:748)
«UNIX Thread» #12 prio=5 os_prio=0 tid=0x00007fb0a4357800 nid=0x1243 waiting for monitor entry [0x00007fb078600000] java.lang.Thread.State: BLOCKED (on object monitor)
at com.da.lect5.deadlock.TwoTasks.lambda$getTask2$1(TwoTasks.java:27)
— waiting to lock (a java.lang.String)
— locked (a java.lang.String)
at com.da.lect5.deadlock.TwoTasks$$Lambda$2/1747585824.run(Unknown
Source)
at java.lang.Thread.run(Thread.java:748)
«Monitor Ctrl-Break» #5 daemon prio=5 os_prio=0 tid=0x00007fb0a42b5800 nid=0x123b runnable [0x00007fb07901f000] java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
— locked (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
— locked (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)
Существует несколько способов снять thread dump:
В листинге 1 можно увидеть, что поток с именем Java Thread заблокирован на мониторе с адресом 0x0000000719bf5760. Важно правильно сопоставить адрес объекта с самим объектом, потому что по шестнадцатеричному значению сделать это невозможно. Для этого можно использовать код, приведенный в листинге 2.
Листинг 2:
Используя класс в листинге 2, можно понять, какой поток на какой блокировке находится. При использовании этого класса следует учесть, что адреса объектов могут меняться после работы сборщика мусора. При анализе потоков необходимо фильтровать потоки, которые создал пользователь, и те, которые запустила сама JVM. Поэтому удобно назначать имена потокам, как это было показано в лекции номер 2. Рекомендуется делать дампы потоков работающего приложения несколько раз, чтоб увидеть изменения состояния потоков. Если одно из ядер процессора загружено на 100 %, следует искать бесконечный цикл или цикл, который очень долго выполняется, обрабатывая большое количество данных. Если предельной загрузки процессора не наблюдается, но какая-то работа все же ожидает выполнения, значит, возник один из видов дедлока или потоки ждут освобождения определенного ресурса.
6.2 Простая взаимная блокировка
Простой дедлок возникает, когда из двух потоков первый захватил блокировку А и пытается захватить блокировку B, а второй захватил блокировку B и пытается захватить блокировку A. Пример такого дедлока приведен в листинге 3.
Листинг 3:
public class DeadLock <
public static void main(String[] args) <
TwoTasks tasks = new TwoTasks();
new Thread(tasks.getTask1(), «Java Thread»).start();
new Thread(tasks.getTask2(), «UNIX Thread»).start();
>
>
В листинге 1 создаются два потока: первый сначала захватывает блокировку на строке str1, а затем — на str2. Второй поток делает то же самое, только в другом порядке. Два потока пытаются захватить блокировки бесконечное количество раз. Рано или поздно наступит дедлок: когда первый поток захватил блокировку на строке “Java” и хочет захватить блокировку на строке “UNIX”. А второй поток уже захватил блокировку на строке “UNIX” и пытается захватить блокировку на строке “Java”. В результате программа в Листинге 1 будет находиться в состоянии взаимной блокировки вечно — т. е. до тех пор пока ее не остановят. Решение в сложившейся ситуации — использовать один и тот же порядок захвата и отпускания блокировок во всех критических секциях программы.
Не стоит использовать в качестве объектов блокировки строки. Это связано с тем, что JVM кэширует строки, объявленные при помощи литералов. Соответственно, строки с одинаковым содержанием будут ссылаться на один и тот же объект, хотя могут быть объявлены в разных частях программы.
6.3 Скрытый дедлок
В разделе 6.1 был рассмотрен случай взаимной блокировки, который виртуальная Java-машина смогла определить, что и было показано в thread dump. Однако могут возникать ситуации, когда Java-машина определить дедлок не может. Рассмотрим такую программу в листинге 4.
Листинг 4:
public class LockOrderingDeadlockSimulator <
public static void main(String[] args) <
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch endSignal = new CountDownLatch(3);
TasksHolder tasks = new TasksHolder();
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.execute(new WorkerThread1(tasks, startSignal, endSignal));
executor.execute(new WorkerThread2(tasks, startSignal, endSignal));
Runnable deadlockDetector =
new ThreadDeadlockDetector(tasks, startSignal, endSignal);
executor.execute(deadlockDetector);
executor.shutdown();
while (!executor.isTerminated()) <
try <
endSignal.await();
> catch (InterruptedException e) <
>
>
public class TasksHolder <
private final Object SHARED_OBJECT = new Object();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void executeTask1() <
// 1. Attempt to acquire a ReentrantReadWriteLock READ lock
lock.readLock().lock();
// Wait 2 seconds to simulate some work.
try <
Thread.sleep(2000);
> catch (InterruptedException any) <
>
try <
// 2. Attempt to acquire a Flat lock.
synchronized (SHARED_OBJECT) <
>
> finally <
lock.readLock().unlock();
>
System.out.println(«executeTask1() :: Work Done!»);
>
public void executeTask2() <
// 1. Attempt to acquire a Flat lock
synchronized (SHARED_OBJECT) <
// Wait 2 seconds to simulate some work.
try <
Thread.sleep(2000);
> catch (InterruptedException any) <
>
// 2. Attempt to acquire a ReentrantReadWriteLock WRITE lock
lock.writeLock().lock();
try <
// Do nothing
> finally <
lock.writeLock().unlock();
>
>
System.out.println(«executeTask2() :: Work Done!»);
>
public ReentrantReadWriteLock getReentrantReadWriteLock() <
return lock;
>
>
public class WorkerThread1 implements Runnable <
private final CountDownLatch startSignal;
private final CountDownLatch endSignal;
private TasksHolder tasks;
public WorkerThread1(TasksHolder tasks, CountDownLatch startSignal,
CountDownLatch endSignal) <
this.tasks = tasks;
this.startSignal = startSignal;
this.endSignal = endSignal;
>
@Override
public void run() <
try <
startSignal.await();
// Execute task #1
tasks.executeTask1();
> catch (InterruptedException e) <
> finally <
endSignal.countDown();
>
>
>
public class WorkerThread2 implements Runnable <
private final CountDownLatch startSignal;
private final CountDownLatch endSignal;
private TasksHolder tasks;
public WorkerThread2(TasksHolder tasks, CountDownLatch startSignal,
CountDownLatch endSignal) <
this.tasks = tasks;
this.startSignal = startSignal;
this.endSignal = endSignal;
>
@Override
public void run() <
try <
startSignal.await();
// Execute task #2
tasks.executeTask2();
> catch (InterruptedException e) <
> finally <
endSignal.countDown();
>
>
>
public class CommonResource <
private Worker owner;
public CommonResource (Worker d) <
owner = d;
>
public Worker getOwner () <
return owner;
>
public synchronized void setOwner (Worker d) <
owner = d;
>
>
public class Worker <
private String name;
private boolean active;
private final Object LOCK = new Object();
public Worker (String name, boolean active) <
this.name = name;
this.active = active;
>
public String getName () <
return name;
>
public boolean isActive () <
return active;
>
Захват свалки java-потока
Узнайте, как захватить дамп java-потока
Захват свалки java-потока
1. Обзор
В этом учебнике мы обсудим различные способы захвата свалки потоков java-приложения.
В следующих разделах мы пройдемся по нескольким инструментам и подходам для создания свалки потоков.
2. Использование утилит JDK
2.1. jstack
jstack — это командная линия УТИЛИТА JDK, которую мы можем использовать для захвата свалки потоков. Он принимает пид процесса и отображает дампа потока в консоли. Кроме того, мы можем перенаправить его выход в файл.
Давайте рассмотрим основной синтаксис команды для захвата свалки потоков с помощью jstack:
Все флаги не являются обязательными. Давайте посмотрим, что они означают:
Давайте будем использовать эти знания, захватив дампа потока и перенаправив результат в файл:
Помните, что мы можем легко получить пид Java-процесса с помощью JPS команда.
2.2. Управление полетами Java
Java Управление полетами (JMC) — это инструмент gui, который собирает и анализирует данные из Java-приложений. После запуска JMC отображается список java-процессов, работающих на локальной машине. Мы также можем подключиться к удаленным Java-процессам через JMC.
Мы можем нажать правой кнопкой мыши на процесс и нажать на ” Начало записи полета ” вариант. После этого Темы вкладка показывает потоковые свалки:
2.3. jvisualvm
Один из его многочисленных вариантов позволяет нам захватить свалку потока. Если мы нажимаем правой кнопкой мыши на процесс Java и выбираем “Thread Dump” опция, инструмент создаст дампа потока и откроет его в новой вкладке:
2.4. jcmd
jcmd это инструмент, который работает, отправляя командные запросы в JVM. Хотя мощный, он не содержит удаленной функциональности – мы должны использовать его в той же машине, где работает процесс Java.
2.5. jconsole
jconsole позволяет нам проверить стек след каждого потока. Если мы откроем jconsole и подключиться к запущенной Java-процессу, мы можем перейти к Темы вкладка и найти след стека каждого потока :
2.6. Резюме
Как оказалось, существует множество способов захвата свалки потоков с помощью утилит JDK. Давайте на минутку поразмыслим над каждым из них и наметим их плюсы и минусы:
3. От командной линии
На серверах корпоративных приложений по соображениям безопасности устанавливается только JRE. Таким образом, мы не можем использовать вышеупомянутые утилиты, поскольку они являются частью JDK. Тем не менее, существуют различные командно-линейные альтернативы, которые позволяют нам легко захватывать свалки потоков.
Самый простой способ захвата свалки потоков в системах, похожих на Unix, — это убить команды, которую мы можем использовать для отправки сигнала в процесс с помощью убить () системный вызов. В этом случае мы отправим его в -3 сигнал.
Используя нашу ту же пид из более ранних примеров, давайте посмотрим, как использовать убить для захвата свалки потоков:
Таким образом, процесс получения сигнала Java будет печатать дампа потока на стандартном выходе.
Если мы забудем процесс Java со следующей комбинацией флагов настройки, то он также перенаправит дампа потока в данный файл:
Теперь, если мы отправим -3 сигнал, в дополнение к стандартному выходу, дамп будет доступен в В/jvm.log файл.
3.2. Ctrl и перерыв (Windows)
Обе эти команды печатают дампа потока на консоль.
4. Программное использование ThreadMxBean
В вышеупомянутой программе мы делаем несколько этапов:
5. Заключение
В этой статье мы показали несколько способов захвата свалки потоков.
Сначала мы обсуждали различные утилиты JDK, а затем альтернативы командной линии. В последнем разделе мы завершили с программным подходом с использованием JMX.
Русские Блоги
Анализ дампа потоков Java
-Он может использоваться на любой платформе операционной системы.
-В большинстве случаев его можно использовать в производственной среде.
-По сравнению с инструментами, предоставляемыми операционной системой, информация, предоставляемая дампом потока Java, проста и напрямую соответствует коду приложения.
-Он мало влияет на анализируемую систему, поэтому может отражать реальные проблемы. Многие другие инструменты профилирования или инструменты сами по себе сильно мешают работе JVM, часто не выявляют реальных проблем, и этот инструмент нельзя использовать в производственной системе.
Поток Java
Создать дамп потока JAVA
DUMP потока JAVA, как снимок текущего процесса JAVA, распечатывает состояние и стек вызовов всех потоков, а также состояние монитора. В разных операционных системах способ создания потока DUMP различается.
windows
Linux(Unix\MacOS)
На каждой платформе операционной системы вы можете использовать jstack
в наборе инструментов JDK 5.0 (или выше).
Здесь следует отметить следующее:
1. Метод создания Thread DUMP и формат файла для разных виртуальных машин JAVA различаются, разные версии JVM имеют разную информацию о дампе.
2. В реальной эксплуатации информации дампа часто недостаточно для подтверждения проблемы. Рекомендуется сгенерировать два или три дампа.Если каждый дамп указывает на одну и ту же проблему, мы можем определить типичность проблемы.
Анализ состояния потока
Deadlock
Поток взаимоблокировки, как правило, относится к ситуации, когда вызовы нескольких потоков вводят взаимное использование ресурсов, что приводит к ситуации, когда он находится в ожидании и не может быть освобожден.
Runnable
Это состояние указывает на то, что поток имеет все условия выполнения, готов к планированию операционной системы в очереди выполнения или работает.
Waiting on condition
Ожидание ресурсов или наступление определенного условия. Конкретные причины необходимо анализировать вместе со stacktrace.
Blocked
Блокировка потока означает, что требуемые ресурсы ждали долгое время, но не были получены во время выполнения текущего потока. Диспетчер потоков контейнера идентифицируется как заблокированное состояние, которое можно понимать как поток, ожидающий ресурса тайм-аут.
Ожидание записи монитора и в Object.wait ()
Сначала посмотрите на потоки в «Entry Set». Мы называем сегмент кода, защищенный синхронизированным, критическим разделом. Когда поток обращается к критическому разделу, он попадает в очередь «Entry Set». Соответствующий код выглядит так:
На данный момент есть две возможности:
· Монитор не принадлежит другим потокам, и в наборе записей нет других ожидающих потоков. Этот поток становится Владельцем монитора соответствующего класса или объекта и выполняет код критической секции.
· Монитор принадлежит другим потокам, и этот поток ожидает в очереди набора записей.
В первом случае поток будет в состоянии «Runnable», а во втором случае поток DUMP будет отображаться как «ожидающий записи монитора». Следующим образом:
«Thread-0» prio=10 tid=0x08222eb0 nid=0x9 waiting for monitor entry [0xf927b000..0xf927bdb8]
— waiting to lock (a java.lang.Object)
— locked (a java.util.ArrayList)
Настройка критического раздела заключается в обеспечении атомарности и целостности выполнения внутреннего кода. Но поскольку критическая секция позволяет потокам проходить только последовательно в любое время, это противоречит первоначальному замыслу нашей многопоточной программы. Если synchronized часто используется в многопоточной программе или используется ненадлежащим образом, это приведет к тому, что большое количество потоков будет ждать на входе в критический раздел, что приведет к значительному падению производительности системы. Если такая ситуация обнаруживается в потоке DUMP, следует просмотреть исходный код и улучшить программу.
Теперь давайте посмотрим, почему поток теперь входит в «Набор ожидания». Когда поток получает Monitor и входит в критическую секцию, если он обнаруживает, что условие для продолжения работы потока не выполняется, он вызывает метод wait () объекта (обычно синхронизированного объекта), закрывает Monitor и входит в очередь «Ожидание». Только когда другие потоки вызывают notify () или notifyAll () для объекта, потоки в очереди «Wait Set» получают возможность конкурировать, но только один поток получает Monitor объекта и возвращается в рабочее состояние. Поток в «Wait Set», DUMP отображается как: в Object.wait (), аналогично:
«Thread-1» prio=10 tid=0x08223250 nid=0xa in Object.wait() [0xef47a000..0xef47aa38]
at java.lang.Object.wait(Native Method)
— waiting on (a java.util.ArrayList)
— locked (a java.util.ArrayList)
Если вы внимательно просмотрите информацию о DUMP выше, вы обнаружите, что в ней есть следующие две строки:
— waiting on (a java.util.ArrayList)
Мне нужно объяснить здесь, почему вы сначала заблокировали этот объект, а затем ждали того же объекта? Давайте посмотрим на код, соответствующий этой теме:
При выполнении потока Монитор этого объекта (соответствующий заблокированному ) сначала получается с помощью synchronized. Когда выполнение достигает obj.wait (), поток отказывается от владения Монитором и входит в очередь «ожидания» (соответствует ожиданию на ).
Часто в вашей программе будет несколько похожих потоков, и все они будут иметь одинаковую информацию DUMP. Это тоже может быть нормально. Например, в программе есть несколько потоков службы, предназначенных для чтения данных запроса из очереди. Эта очередь является объектом блокировки и ожидания. Когда очередь пуста, эти потоки будут ждать в этой очереди, пока в очереди не появятся данные, и эти потоки не будут уведомлены.Конечно, только один поток получает блокировку и продолжает выполнение, в то время как другие потоки продолжают ждать.
Исчерпывающий пример
Комплексная демонстрация:Deadlock
После получения дампа потока java все, что вам нужно сделать, это найти поток «ожидание записи монитора». Если большое количество потоков ожидают блокировки одного и того же адреса (поскольку для Java существует только одна блокировка для объекта ), значит, возможно, возник тупик. такие как:
«service-j2ee» prio=5 tid=0x024f1c28 nid=0x125 waiting for monitor entry
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
lock (a com.sun.enterprise.resource.IASNonSharedResourcePool)
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
Чтобы определить проблему, часто необходимо снова собрать дамп потока через две минуты.Если результат такой же, значит, все еще существует большое количество потоков, ожидающих блокировки одного и того же адреса, значит, это тупиковая ситуация.
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: «Thread-20» daemon prio=5 tid=0x01394f18
nid=0x109 runnable [6716f000..6716fc28]
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at oracle.net.ns.Packet.receive(Unknown
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: a
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: a
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
[27/Jun/2006:10:03:08] WARNING (26140): CORE3283: stderr: at
В этом примере поток, удерживающий блокировку, ожидает, пока Oracle вернет результат, но никогда не ждет ответа, поэтому возникает взаимоблокировка.
Если поток, удерживающий блокировку, все еще ожидает блокировки другого объекта, следуйте описанному выше методу, пока не будет найдена основная причина взаимоблокировки.
Комплексная демонстрация 2: ожидание блокировки и блокировка
Комплексная демонстрация 4: в Obejct.wait () И TIMED_WAITING
Конкретные примеры
на основании приведенного выше анализа:
Все потоки в «Entry Set» ожидают получения Monitor, и поток, который его получает, становится Runnable-потоком, в противном случае он всегда будет в «ожидании записи монитора». В качестве примера взят следующий фрагмент кода:
public class MyThread implements Runnable<
for (int i = 0; i (a org.marshal.MyThread)
at java.lang.Thread.run(Thread.java:636)
«A» prio=10 tid=0x09698800 nid=0x11d5 runnable [0x8bb73000]
java.lang.Thread.State: RUNNABLE
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(FileOutputStream.java:297)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
— locked (a java.io.BufferedOutputStream)
at java.io.PrintStream.write(PrintStream.java:449)
— locked (a java.io.PrintStream)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:220)
at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:290)
at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:103)
— locked (a java.io.OutputStreamWriter)
at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
at java.io.PrintStream.write(PrintStream.java:494)
— locked (a java.io.PrintStream)
at java.io.PrintStream.print(PrintStream.java:636)
at java.io.PrintStream.println(PrintStream.java:773)
— locked (a java.io.PrintStream)
at org.marshal.MyThread.run(MyThread.java:8)
— locked (a org.marshal.MyThread)
at java.lang.Thread.run(Thread.java:636)
— монитор, за который конкурируют два потока.
Все потоки в «Ожидании» с нетерпением ждут Монитор. Как они попали в «Ждущий сет»? Когда поток получает Monitor, но когда другие ресурсы отсутствуют, он вызывает метод wait () синхронизированного объекта блокировки (обычно объект в synchronized ()), закрывает Monitor и входит в «Набор ожидания» очередь. Только когда другие потоки используют notify () или notifyAll () для снятия блокировки синхронизации, этот поток снова получит возможность конкурировать за Monitor. В стеке его состояние находится в Object.wait (). Измените приведенный выше код следующим образом:
> catch (InterruptedException e) <
// TODO Auto-generated catch block
public static void main(String[] args) <
WaitThread t1 = new WaitThread();
Thread ta = new Thread(t1, «A»);
Thread tb = new Thread(t1, «B»);
Получите файл дампа таким же образом, его содержимое выглядит следующим образом:
«B» prio=10 tid=0x08173000 nid=0x1304 in Object.wait() [0x8baf2000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
— waiting on (a org.marshal.WaitThread)
at java.lang.Object.wait(Object.java:502)
at org.marshal.WaitThread.run(WaitThread.java:8)
— locked (a org.marshal.WaitThread)
at java.lang.Thread.run(Thread.java:636)
«A» prio=10 tid=0x08171c00 nid=0x1303 in Object.wait() [0x8bb43000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
— waiting on (a org.marshal.WaitThread)
at java.lang.Object.wait(Object.java:502)
at org.marshal.WaitThread.run(WaitThread.java:8)
— locked (a org.marshal.WaitThread)
at java.lang.Thread.run(Thread.java:636)
Основываясь на проблеме тупика, которую мы часто обсуждаем, создайте фрагмент кода следующим образом
public class DeadThread implements Runnable<
private Object monitor_A = new Object();
private Object monitor_B = new Object();
public void method_A()<
System.out.println(Thread.currentThread().getName()+» invoke method A»);
public void method_B()<
System.out.println(Thread.currentThread().getName()+» invoke method B»);
for(int i=0;i (a java.lang.Object)
— locked (a java.lang.Object)
at org.marshal.DeadThread.run(DeadThread.java:28)
at java.lang.Thread.run(Thread.java:636)
«A» prio=10 tid=0x0898b800 nid=0x2699 waiting for monitor entry [0x8baf3000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.marshal.DeadThread.method_B(DeadThread.java:19)
— waiting to lock (a java.lang.Object)
— locked (a java.lang.Object)
at org.marshal.DeadThread.run(DeadThread.java:29)
at java.lang.Thread.run(Thread.java:636)
В то же время обратите внимание, что информация в конце трассировки стека:
Found one Java-level deadlock:
=============================
«B»:
waiting to lock monitor 0x089615d8 (object 0xaa4d6f88, a java.lang.Object),
which is held by «A»
«A»:
waiting to lock monitor 0x08962258 (object 0xaa4d6f80, a java.lang.Object),
which is held by «B»
Java stack information for the threads listed above:
===================================================
«B»:
at org.marshal.DeadThread.method_A(DeadThread.java:11)
— waiting to lock (a java.lang.Object)
— locked (a java.lang.Object)
at org.marshal.DeadThread.run(DeadThread.java:28)
at java.lang.Thread.run(Thread.java:636)
«A»:
at org.marshal.DeadThread.method_B(DeadThread.java:19)
— waiting to lock (a java.lang.Object)
— locked (a java.lang.Object)
at org.marshal.DeadThread.run(DeadThread.java:29)
at java.lang.Thread.run(Thread.java:636)
О взаимоблокировках уровня Java напрямую сообщается в стеке.
Возможно, просмотр файла дампа напрямую не является интуитивным. Для анализа рекомендуется использовать инструмент анализа javacode от ibm. Например, файл дампа, созданный с помощью вышеуказанного кода взаимоблокировки, помещается в инструмент анализа и отображается следующим образом:
Найти проблему проще с помощью графического интерфейса.