Comment puis-je par programme détecter qu'un blocage s'est produit dans un programme Java?
Vous pouvez le faire par programmation en utilisant le ThreadMXBean
fourni avec le 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.
}
}
Évidemment, vous devez essayer d'isoler le thread qui effectue cette vérification de blocage - Sinon, si ce thread se bloque, il ne pourra pas exécuter la vérification!
C'est d'ailleurs ce que JConsole utilise sous les couvertures.
Un indice utile pour l'enquête:
Si vous pouvez attraper l'application en flagrant délit et suspecter un blocage, allez et appuyez sur "Ctrl-Break" dans la fenêtre de la console Java.exe (ou "Ctrl- \" sous Solaris/Linux). Le jvm va vider l'état actuel et la trace de pile de tous les threads, trouver les verrous morts et les décrire avec précision.
Cela ressemblera à ceci:
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)
...
Vous pouvez détecter les threads bloqués par programmation à l'aide de la classe ThreadMXBean. Voici le code,
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");
}
Cliquez sur ici pour plus d'informations sur la façon de détecter les threads bloqués.
JArmus est une bibliothèque pour la détection et l'évitement des blocages. Il inclut la prise en charge de: Thread.join
, CyclicBarrier
, CountDownLatch
, Phaser
et ReentrantLock
.
Pour utiliser JArmus, vous devez instrumenter votre code. Soit via l'une de ses classes instrumentées, soit automatiquement avec l'instrumentar JArmus jarmusc
.
Java -jar jarmusc.jar yourprogram.jar checkedprogram.jar
L'entrée yourprogram.jar
Est le programme que vous souhaitez vérifier. La sortie est le même programme avec des contrôles pour trouver automatiquement tout blocage.
La vérification des blocages avec les classes CyclicBarrier
, CountDownLatch
, Phaser
est un peu délicate --- par exemple, JConsole ne peut pas détecter ces types de blocages. JArmus a besoin d'un peu d'aide de votre part: vous devez spécifier quels threads influencent la synchronisation, nous les appelons threads enregistrés .
Dès que possible, le thread doit se marquer comme enregistré. Un bon endroit pour marquer les threads enregistrés est à la méthode de début Runnable.run
. JArmus.register(latch);
Le programme suivant qui interbloque est correctement identifié par 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();
Vous voudrez peut-être considérer MTRAT d'IBM . Il vaut mieux prévenir que guérir après tout. Kit de développement logiciel multicœur est également livré avec un outil de détection de blocage.
Si vous n'avez pas besoin de détection programmatique, vous pouvez le faire via JConsole; sur l'onglet du fil, il y a un bouton "détecter un blocage". Dans JDK6, cela détecte les verrous pour les moniteurs intrinsèques et j.u.c
Lock
s
Exécutez la JConsole via le $Java_HOM/bin/jconsole
commande
tempus-fugit l'implémente également avec une classe de vidage de thread programmatique. Il est implémenté à l'aide du mécanisme mbean mentionné ci-dessus et offre une solution de super duper prête à l'emploi.
Il y a du code ici: http://www.Java2s.com/Code/Java/Development-Class/PerformingdeadlockdetectionprogrammaticallywithintheapplicationusingthejavalangmanagementAPI.htm
La magie opère dans 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;
}
Cela appelle une API de ThreadMXBean
qui a un nom différent dans Java 5 et 6 (d'où la if()
) externe).
L'exemple de code permet également d'interrompre les verrous, de sorte que vous pouvez même sortir de l'impasse.
Dans le cas où vous souhaitez que cela soit fait au moment de l'exécution, vous pouvez utiliser watchdog pour cela.