Il y a longtemps, j'ai sauvé une phrase d'un livre de référence Java: "Java n'a pas de mécanisme pour gérer le blocage. Il ne saura même pas qu'un blocage s'est produit." (Head First = Java 2e édition, p.516)
Alors, qu'en est-il? Existe-t-il un moyen d'attraper un blocage dans Java? Je veux dire, y a-t-il un moyen pour notre code de comprendre qu'un cas de blocage s'est produit?
Depuis JDK 1.5, le package Java.lang.management
Contient des méthodes très utiles pour rechercher et inspecter les blocages qui se produisent. Voir les méthodes findMonitorDeadlockedThreads()
et findDeadlockedThreads()
de la classe ThreadMXBean
.
Une façon possible de l'utiliser est d'avoir un thread de surveillance distinct (ou une tâche périodique) qui le fait.
Exemple de code:
ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
long[] ids = tmx.findDeadlockedThreads();
if (ids != null) {
ThreadInfo[] infos = tmx.getThreadInfo(ids, true, true);
System.out.println("The following threads are deadlocked:");
for (ThreadInfo ti : infos) {
System.out.println(ti);
}
}
JConsole est capable de détecter les blocages dans une application en cours d'exécution.
Les JDK 5 et 6 afficheront les informations sur les verrous maintenus dans un vidage de thread complet (obtenu avec kill -3, jstack, jconsole, etc.). JDK 6 contient même des informations sur ReentrantLock et ReentrantReadWriteLock. Il est possible à partir de ces informations de diagnostiquer un blocage en trouvant un cycle de verrouillage: le thread A détient le verrou 1, le thread B détient le verrou 2, et soit A demande 2 ou B demande 1. D'après mon expérience, cela est généralement assez évident.
D'autres outils d'analyse peuvent en fait trouver des blocages potentiels même s'ils ne se produisent pas. Les outils de discussion de fournisseurs tels que OptimizeIt, JProbe, Coverity, etc. sont de bons endroits à regarder.
Notez qu'il existe un type de blocage utilisant le package simultané qui est très difficile à déboguer. C'est là que vous avez un ReentrantReadWriteLock et un thread saisit le verrou de lecture, puis (disons) essaie d'entrer dans un moniteur détenu par un autre thread qui attend également d'attraper le verrou d'écriture. Ce qui rend particulièrement difficile le débogage, c'est qu'il n'y a aucune trace de qui a entré un verrou de lecture. C'est simplement un compte. Le thread a peut-être même levé une exception et est mort en laissant le nombre de lectures non nul.
Voici un exemple de blocage que la méthode findDeadlockedThreads mentionnée précédemment n'obtiendra pas:
import Java.util.concurrent.locks.*;
import Java.lang.management.*;
public class LockTest {
static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public static void main(String[] args) throws Exception {
Reader reader = new Reader();
Writer writer = new Writer();
sleep(10);
System.out.println("finding deadlocked threads");
ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
long[] ids = tmx.findDeadlockedThreads();
if (ids != null) {
ThreadInfo[] infos = tmx.getThreadInfo(ids, true, true);
System.out.println("the following threads are deadlocked:");
for (ThreadInfo ti : infos) {
System.out.println(ti);
}
}
System.out.println("finished finding deadlocked threads");
}
static void sleep(int seconds) {
try {
Thread.currentThread().sleep(seconds*1000);
} catch (InterruptedException e) {}
}
static class Reader implements Runnable {
Reader() {
new Thread(this).start();
}
public void run() {
sleep(2);
System.out.println("reader thread getting lock");
lock.readLock().lock();
System.out.println("reader thread got lock");
synchronized (lock) {
System.out.println("reader thread inside monitor!");
lock.readLock().unlock();
}
}
}
static class Writer implements Runnable {
Writer() {
new Thread(this).start();
}
public void run() {
synchronized (lock) {
sleep(4);
System.out.println("writer thread getting lock");
lock.writeLock().lock();
System.out.println("writer thread got lock!");
}
}
}
}
Si vous êtes sur Java 5, vous pouvez appeler la méthode findMonitorDeadlockedThreads()
sur le ThreadMXBean que vous pouvez obtenir via un appel de Java.lang.management.ManagementFactory.getThreadMXBean()
. Cela trouvera des blocages causé par les moniteurs d'objets uniquement. Sur Java 6, il y a findDeadlockedThreads()
qui trouvera également des interblocages causés par des "synchroniseurs propriétaires (par exemple ReentrandLock
et ReentrantReadWriteLock
).
N'oubliez pas qu'il sera probablement coûteux d'appeler ces méthodes, elles ne doivent donc être utilisées qu'à des fins de dépannage.
Les interblocages peuvent être évités si vous suivez une règle simple: demandez à tous les threads de réclamer et de libérer leurs verrous dans le même ordre. De cette façon, vous n'entrez jamais dans une situation où un blocage peut se produire.
Même le problème des philosophes de la restauration peut être considéré comme une violation de cette règle car il utilise des concepts relatifs de cuillère gauche et droite qui entraînent des threads différents utilisant différents ordres d'allocation des cuillères. Si les cuillères étaient numérotées de façon unique et que les philosophes essayaient tous d'obtenir la cuillère la moins numérotée en premier, l'impasse serait impossible.
À mon avis, mieux vaut prévenir que guérir.
C'est l'une des deux directives que j'aime suivre pour garantir le bon fonctionnement des threads. L'autre est de s'assurer que chaque thread est niquement responsable de sa propre exécution car il est le seul pleinement conscient de ce qu'il fait à un moment donné.
Cela signifie donc pas de Thread.stop
appels, utilisez un indicateur global (ou une file d'attente de messages ou quelque chose comme ça) pour indiquer à un autre thread que vous souhaitez que des mesures soient prises. Ensuite, laissez ce fil faire le travail réel.
Pas exactement ce que vous avez demandé, mais lorsqu'un blocage se produit se produit, vous pouvez faire un "kill -3" sur l'ID de processus et il transfère un vidage de thread vers stdout. De plus, le 1.6 jvm a quelques outils pour faire la même chose de manière graphique.
Si vous exécutez à partir de la ligne de commande et que vous pensez être bloqué, essayez ctrl + break dans les fenêtres (ctrl +\sous unix) pour obtenir un vidage de thread. Voir http://Java.Sun.com/javase/6/webnotes/trouble/TSG-VM/html/gbmps.html
Java peut détecter les blocages (bien qu'il ne soit pas au moment de l'exécution, il peut toujours le diagnostiquer et le signaler).
Par exemple, lorsque vous utilisez une version légèrement modifiée du code 'Saurabh M. Chande' ci-dessous (l'a changé en Java et a ajouté un certain temps pour garantir un verrouillage à chaque exécution). Une fois que vous l'exécutez et il se bloque, si vous tapez:
kill -3 PID # where 'PID' is the Linux process ID
Il générera un vidage de pile, qui comprendra les informations suivantes:
Found one Java-level deadlock:
=============================
"Thread-0":
waiting to lock monitor 0x08081670 (object 0x7f61ddb8, a Deadlock$A),
which is held by "main"
"main":
waiting to lock monitor 0x080809f0 (object 0x7f61f3b0, a Deadlock$B),
which is held by "Thread-0"
Dr. Heinz Kabutz de JavaSpecialists a écrit un divertissant et informatif numéro de newsletter sur Java deadlocks et décrit quelque chose appelé ThreadMXBean dans n autre numéro de newsletter Entre ceux-ci, vous devriez avoir une bonne idée des problèmes et quelques conseils pour faire votre propre instrumentation.
Si vous déboguez dans Eclipse, vous pouvez suspendre l'application (sélectionnez l'application dans la vue de débogage et le petit bouton || de la barre d'outils de débogage), puis il peut signaler des blocages.
Voir http://runnerwhocodes.blogspot.com/2007/10/deadlock-detection-with-Eclipse.html pour un exemple.
Java 5 a introduit ThreadMXBean - une interface qui fournit diverses méthodes de surveillance des threads. ... La différence est que findDeadlockedThreads peut également détecter les interblocages causés par les verrous du propriétaire (Java.util.concurrent), tandis que findMonitorDeadlockedThreads ne peut détecter que les verrous du moniteur (c'est-à-dire les blocs synchronisés)
Ou vous pouvez le détecter par programme, reportez-vous à ceci https://dzone.com/articles/how-detect-Java-deadlocks