Как я могу программным образом определить, что в программе Java произошел тупик?
Программное обнаружение тупика в java
Ответ 1
Вы можете сделать это программно, используя 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 использует под обложками.
Ответ 2
Один полезный намек для исследования:
Если вы можете поймать приложение красным цветом и подозревать, что произошел тупик, перейдите и нажмите "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)
...
Ответ 3
JArmus - это библиотека для обнаружения и предотвращения взаимоблокировок. Он включает поддержку:
Thread.join
, CyclicBarrier
, CountDownLatch
, Phaser
и
ReentrantLock
.
Чтобы использовать JArmus, вам нужно измерить свой код. Либо через один из его инструментальных классов, либо автоматически с инструментами JArmus jarmusc
.
java -jar jarmusc.jar yourprogram.jar checkedprogram.jar
Ввод yourprogram.jar
- это программа, которую вы хотите проверить.
Вывод - это та же самая программа с проверками, чтобы автоматически найти тупик.
Барьеры нуждаются в помощи
Проверка взаимоблокировок с классами CyclicBarrier
, CountDownLatch
, Phaser
немного сложна - например, JConsole не может обнаружить эти типы взаимоблокировок. JArmus нуждается в небольшой помощи от вас: вы должны указать, какие потоки влияют на синхронизацию, мы называем эти зарегистрированные потоки.
Как можно скорее, поток должен отмечать себя как зарегистрированный. Хорошим местом для отметки зарегистрированных потоков является метод begin Runnable.run
.
JArmus.register(latch);
Пример
Следующая программа, в которой взаимоблокировки правильно идентифицированы 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();
Ответ 4
Возможно, вы захотите рассмотреть IBM MTRAT. Профилактика лучше, чем лечение в конце концов. Multicore Software Development Kit также поставляется с инструментом обнаружения блокировки.
Ответ 5
Если вам не требуется программное обнаружение, вы можете сделать это с помощью JConsole; на вкладке потока есть кнопка "обнаружить тупик". В JDK6 это обнаружение блокирует как внутренние мониторы, так и j.u.c
Lock
s
Запустите JConsole с помощью команды $JAVA_HOM/bin/jconsole
Ответ 6
Вы можете обнаружить заторможенные потоки программным способом, используя класс 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");
}
Нажмите здесь для получения дополнительной информации о том, как обнаружить заторможенные потоки.
Ответ 7
tempus-fugit также реализует его вместе с программным демпинговым классом. Он был реализован с использованием механизма mbean, упомянутого выше, и предлагает решение, основанное на суперсовременности.
Ответ 8
Магия происходит в 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()
).
Пример кода также позволяет прерывать блокировки, поэтому вы можете даже выйти из тупика.
Ответ 9
Если вы хотите, чтобы это было сделано во время выполнения, вы можете использовать watchdog для этого.