Программное обнаружение взаимоблокировок в Java
-
12-09-2019 - |
Вопрос
Как я могу программно обнаружить, что в программе Java возникла взаимоблокировка?
Нет правильного решения
Другие советы
Вы можете сделать это программно, используя команду ThreadMXBean
который поставляется с JDK:
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
long[] threadIds = bean.findDeadlockedThreads(); // Returns null if no threads are deadlocked.
if (threadIds != null) {
ThreadInfo[] infos = bean.getThreadInfo(threadIds);
for (ThreadInfo info : infos) {
StackTraceElement[] stack = info.getStackTrace();
// Log or store stack trace information.
}
}
Очевидно, вам следует попытаться изолировать поток, выполняющий эту проверку взаимоблокировки. В противном случае, если этот поток заблокируется, он не сможет выполнить проверку!
Кстати, именно это и использует JConsole.
Один полезный совет для расследования:
Если вы можете поймать приложение с поличным и подозреваете, что произошла взаимоблокировка, нажмите «Ctrl-Break» в окне консоли java.exe (или «Ctrl-\" в Solaris/Linux).JVM будет выгружать текущий статус и трассировку стека всех потоков, обнаруживать мертвые блокировки и точно описывать их.
Это будет выглядеть примерно так:
Full thread dump Java HotSpot(TM) Client VM (1.5.0_09-b03 mixed mode):
"[Test Timer] Request Queue" prio=6 tid=0x13d708d0 nid=0x1ec in Object.
wait() [0x1b00f000..0x1b00fb68]
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Unknown Source)
at library.util.AsyncQueue.run(AsyncQueue.java:138)
- locked <0x02e70000> (a test.server.scheduler.SchedulerRequestQueue)
...
Found one Java-level deadlock:
=============================
"Corba service":
waiting to lock monitor 0x13c06684 (object 0x04697d90, a java.lang.Object),
which is held by "[Server Connection] Heartbeat Timer"
"[Server Connection] Heartbeat Timer":
waiting to lock monitor 0x13c065c4 (object 0x0467e728, a test.proxy.ServerProxy), which is held by "Corba service"
Java stack information for the threads listed above:
===================================================
"Corba service":
at test.proxy.ServerProxy.stopHBWatchDog(ServerProxy:695)
- waiting to lock <0x04697d90> (a java.lang.Object)
...
Вы можете обнаружить тупиковые потоки программно, используя класс ThreadMXBean. Вот код:
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
long ids[] = bean.findMonitorDeadlockedThreads();
if(ids != null)
{
ThreadInfo threadInfo[] = bean.getThreadInfo(ids);
for (ThreadInfo threadInfo1 : threadInfo)
{
System.out.println(threadInfo1.getThreadId()); //Prints the ID of deadlocked thread
System.out.println(threadInfo1.getThreadName()); //Prints the name of deadlocked thread
System.out.println(threadInfo1.getLockName()); //Prints the string representation of an object for which thread has entered into deadlock.
System.out.println(threadInfo1.getLockOwnerId()); //Prints the ID of thread which currently owns the object lock
System.out.println(threadInfo1.getLockOwnerName()); //Prints name of the thread which currently owns the object lock.
}
}
else
{
System.out.println("No Deadlocked Threads");
}
Нажмите здесь для получения дополнительной информации о том, как обнаружить тупиковые потоки.
Джармус — это библиотека для обнаружения и предотвращения взаимоблокировок.Он включает поддержку:Thread.join
, CyclicBarrier
, CountDownLatch
, Phaser
, иReentrantLock
.
Чтобы использовать JArmus, вам необходимо инструментировать свой код.Либо через один из инструментальных классов, либо автоматически с помощью инструментария JArmus. jarmusc
.
java -jar jarmusc.jar yourprogram.jar checkedprogram.jar
Вход yourprogram.jar
это программа, которую вы хотите проверить.На выходе получается та же программа с проверками на автоматическое обнаружение любой тупиковой ситуации.
Барьерам нужна помощь
Проверка взаимоблокировок с помощью классов CyclicBarrier
, CountDownLatch
, Phaser
это немного сложно - например, JConsole не может обнаружить эти типы взаимоблокировок.Джармасу нужна ваша небольшая помощь:вы должны указать, какие потоки влияют на синхронизацию, мы называем их зарегистрированный потоки.
Как можно скорее поток должен пометить себя как зарегистрированный.Хорошее место для пометки зарегистрированных тем — это метод «начало». Runnable.run
.
JArmus.register(latch);
Пример
Следующая программа, которую Jarmus правильно идентифицируется: Jarmus правильно идентифицируется:
final CountDownLatch latch = new CountDownLatch(2);
final CyclicBarrier barrier = new CyclicBarrier(2);
final Queue<Exception> exceptions = new ArrayDeque<>();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
JArmus.register(barrier); // do not forget to register!
JArmus.register(latch); // do not forget to register!
latch.countDown();
latch.await();
barrier.await();
} catch (Exception e) {
exceptions.add(e);
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
JArmus.register(barrier); // do not forget to register!
JArmus.register(latch); // do not forget to register!
barrier.await();
latch.countDown();
latch.await();
} catch (Exception e) {
exceptions.add(e);
}
}
});
t1.start();
t2.start();
Возможно, вы захотите рассмотреть MTRAT от IBM.В конце концов, профилактика лучше, чем лечение.А Комплект разработки многоядерного программного обеспечения также поставляется с инструментом обнаружения взаимоблокировок.
Если вам не требуется программное обнаружение, вы можете сделать это через JКонсоль;на вкладке темы есть кнопка "обнаружить взаимоблокировку".В JDK6 это обнаруживает блокировки как для встроенных мониторов, так и для j.u.c
Lock
с
Запустите JConsole через $JAVA_HOM/bin/jconsole
команда
Волшебство происходит в ThreadMonitor.findDeadlock()
:
public boolean findDeadlock() {
long[] tids;
if (findDeadlocksMethodName.equals("findDeadlockedThreads")
&& tmbean.isSynchronizerUsageSupported()) {
tids = tmbean.findDeadlockedThreads();
if (tids == null) {
return false;
}
System.out.println("Deadlock found :-");
ThreadInfo[] infos = tmbean.getThreadInfo(tids, true, true);
for (ThreadInfo ti : infos) {
printThreadInfo(ti);
printLockInfo(ti.getLockedSynchronizers());
System.out.println();
}
} else {
tids = tmbean.findMonitorDeadlockedThreads();
if (tids == null) {
return false;
}
ThreadInfo[] infos = tmbean.getThreadInfo(tids, Integer.MAX_VALUE);
for (ThreadInfo ti : infos) {
// print thread information
printThreadInfo(ti);
}
}
return true;
}
Это вызывает API ThreadMXBean
который имеет другое имя в Java 5 и 6 (отсюда и внешний if()
).
Пример кода также позволяет прерывать блокировки, так что вы даже можете выйти из тупика.
темпус-фугит также реализует его вместе с классом программного дампа потока.Он реализован с использованием механизма mbean, упомянутого выше, и предлагает готовое супер-пупер решение.
Если вы хотите, чтобы это было сделано во время выполнения, вы можете использовать сторожевая собака для этого.