Gestion de L'Exception interrompue en Java
Quelle est la différence entre les façons suivantes de manipuler InterruptedException
? Quelle est la meilleure façon de le faire?
try{
//...
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
ou
try{
//...
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
EDIT: j'aimerais aussi savoir dans quels scénarios ces deux-là sont utilisés.
7 réponses
vous êtes probablement venu pour poser cette question parce que vous avez appelé une méthode qui jette InterruptedException
.
tout d'abord, vous devriez voir throws InterruptedException
pour ce qu'il est: une partie de la signature de la méthode et un résultat possible d'appeler la méthode que vous appelez. Donc, commencer par embrasser le fait qu'un InterruptedException
est parfaitement valable résultat de l'appel de méthode.
maintenant, si la méthode que vous appelez jette une telle exception, ce qui devrait votre méthode fait l'affaire? Vous pouvez trouver la réponse en pensant à ce qui suit:
est-ce que la méthode vous est en train de mettre en œuvre pour lancer un InterruptedException
? autrement dit, est-ce qu'un InterruptedException
est un résultat raisonnable quand on appelle votre méthode ?
-
si Oui , puis
throws InterruptedException
devrait faire partie de votre signature de la méthode, et vous devriez laisser l'exception se propager (c.-à-d. ne l'attrapez pas du tout).exemple : votre méthode attend une valeur du réseau pour terminer le calcul et retourner un résultat. Si l'appel réseau de blocage lance un
InterruptedException
votre méthode ne peut pas terminer le calcul d'une manière normale. Vous laissez leInterruptedException
se propager.int computeSum(Server server) throws InterruptedException { // Any InterruptedException thrown below is propagated int a = server.getValueA(); int b = server.getValueB(); return a + b; }
-
si Non , alors vous ne devriez pas déclarer votre méthode avec
throws InterruptedException
et vous devriez (doit!) intercepter l'exception. Maintenant, deux choses sont importantes à garder à l'esprit dans cette situation:-
Quelqu'un a interrompu votre fil. Que quelqu'un est probablement impatient d'annuler l'opération, de mettre fin au programme avec élégance, ou peu importe. Tu devrais être poli avec cette personne et revenir. à partir de votre méthode sans plus tarder.
-
même si votre "méthode 1519350920" peut produire une valeur de retour raisonnable dans le cas d'un
InterruptedException
le fait que le fil ait été interrompu peut encore être important. En particulier, le code qui appelle votre méthode peut-être intéressé de savoir si une interruption s'est produite pendant l'exécution de votre méthode. Vous devez donc noter le fait qu'une interruption a eu lieu par mise en place du drapeau interrompu:Thread.currentThread().interrupt()
Exemple : L'utilisateur a demandé d'imprimer une somme de deux valeurs. L'impression "
Failed to compute sum
" est acceptable si la somme ne peut pas être calculée (et beaucoup mieux que de laisser le programme s'écraser avec une trace de pile due à unInterruptedException
). En d'autres termes, il ne pas sens de déclarer cette méthode avecthrows InterruptedException
.void printSum(Server server) { try { int sum = computeSum(server); System.out.println("Sum: " + sum); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag System.out.println("Failed to compute sum"); } }
-
maintenant, il devrait être clair que faire throw new RuntimeException(e)
est une mauvaise idée. Il n'est pas très poli de l'appelant. Vous pouvez inventer une nouvelle exception d'exécution, mais la cause racine (quelqu'un veut que le thread arrête l'exécution) pourrait se perdre.
autres exemples:
mise en œuvre
Runnable
: comme vous pouvez avoir découverte, la signature deRunnable.run
ne permet pas de réécrireInterruptedExceptions
. Eh bien, vous signé sur la mise en œuvreRunnable
, ce qui signifie que vous signé pour traiter avec possibleInterruptedExceptions
. Choisir une interface différente, commeCallable
, ou suivez la deuxième approche ci-dessus.
Appel
Thread.sleep
: Vous tentez de lire un fichier et de la spec dit que vous devriez essayer 10 fois avec 1 seconde entre les deux. Vous appelezThread.sleep(1000)
. Donc, vous devez traiter avecInterruptedException
. Pour une méthode telle quetryToReadFile
il est parfaitement logique de dire, " si je suis interrompu, Je ne peux pas compléter mon action d'essayer de lire le fichier " ". En d'autres termes, il est parfaitement logique pour la méthode à jeterInterruptedExceptions
.String tryToReadFile(File f) throws InterruptedException { for (int i = 0; i < 10; i++) { if (f.exists()) return readFile(f); Thread.sleep(1000); } return null; }
Ce post a été réécrit un article ici .
comme il se trouve que j'étais en train de lire à ce sujet ce matin sur mon chemin pour travailler dans Java Concourency In Practice par Brian Goetz. Fondamentalement, il dit que vous devez faire l'une des deux choses
-
propagez le
InterruptedException
- Déclarez votre méthode pour lancer le vérifiéInterruptedException
de sorte que votre interlocuteur doit traiter avec elle. -
Restaurer L'interruption - parfois vous ne pouvez pas jeter
InterruptedException
. Dans ces cas, vous devez attraper leInterruptedException
et restaurer l'état d'interruption en appelant la méthodeinterrupt()
sur lecurrentThread
afin que le code plus haut dans la pile d'appels puisse voir qu'une interruption a été émise.
Qu'essayez-vous de faire?
le InterruptedException
est lancé lorsqu'un fil attend ou dort et qu'un autre fil l'interrompt en utilisant la méthode interrupt
dans la classe Thread
. Donc si vous attrapez cette exception, cela signifie que le fil a été interrompu. Habituellement, il n'y a aucun point à appeler Thread.currentThread().interrupt();
de nouveau, à moins que vous vouliez vérifier le statut "interrompu" du fil d'un autre endroit.
concernant votre autre option de jeter "un 151940920" , il ne semble pas une très bonne chose à faire (qui va attraper? comment peut-elle être traitée?) mais il est difficile d'en dire plus sans informations supplémentaires.
pour moi, la chose clé à propos de ceci est: une Exceptionintervenue n'est pas quelque chose qui va mal, c'est le fil qui fait ce que vous lui avez dit de faire. Il n'y a donc aucun sens à le repenser dans Uneexception RuntimeException.
dans de nombreux cas, il est logique de repenser une exception enveloppé dans une RuntimeException quand vous dites, Je ne sais pas ce qui a mal tourné ici et je ne peux rien faire pour le réparer, je veux juste qu'il sortir du flux de traitement actuel et frapper n'importe quoi gestionnaire d'exception pour l'ensemble de l'application que j'ai pour qu'il puisse l'enregistrer. Ce n'est pas le cas avec une InterruptedException, c'est juste le thread qui répond à l'appel d'interruption (), c'est lancer L'InterruptedException afin d'aider à annuler le traitement du thread en temps opportun.
ainsi propagez L'Exception interrompue, ou mangez-la intelligemment (c'est-à-dire à un endroit où elle aura accompli ce qu'elle était censée faire) et réinitialisez le drapeau d'interruption. Notez que l' le drapeau d'interruption est effacé quand L'InterruptedException est lancée; l'hypothèse que font les développeurs de la bibliothèque Jdk est que saisir l'exception revient à la manipuler, donc par défaut le drapeau est effacé.
donc certainement la première voie est meilleure, le second exemple posté dans la question n'est pas utile à moins que vous ne vous attendiez pas à ce que le thread soit réellement interrompu, et l'interrompre revient à une erreur.
Voici une réponse que j'ai écrite décrivant comment les interruptions de travail, avec un exemple . Vous pouvez voir dans le code d'exemple où il utilise L'InterruptedException pour sortir d'une boucle while dans la méthode d'exécution du Runnable.
le choix par défaut correct est add InterruptedException à votre liste de lancements. Une interruption indique qu'un autre thread souhaite que votre thread se termine. La raison de cette demande n'est pas évidente et est entièrement contextuelle, donc si vous n'avez aucune connaissance supplémentaire, vous devez supposer que c'est juste un arrêt amical, et tout ce qui évite cet arrêt est une réponse non amicale.
Java ne jettera pas au hasard InterruptedException, tous les conseils seront ne pas affecter votre application, mais j'ai rencontré un cas où le développeur de suivre la stratégie "avalent" est devenu très incommode. Une équipe avait mis au point un grand nombre de tests et utilisé du fil.Dormir beaucoup. Maintenant, nous avons commencé à exécuter les tests dans notre serveur CI, et parfois en raison de défauts dans le code se retrouvait dans des attentes permanentes. Pour aggraver la situation, en essayant d'annuler le travail de CI, il n'a jamais fermé parce que le fil.Interruption qui était destinée à annuler le test n'a pas avorté emploi. Nous avons dû nous connecter à la boîte et tuer manuellement les processus.
pour faire court, si vous lancez simplement L'Exception interrompue, vous correspondrez à l'intention par défaut que votre thread devrait se terminer. Si vous ne pouvez pas ajouter InterruptedException à votre liste de lancements, Je l'envelopperais dans une RuntimeException.
il y a un argument très rationnel à faire valoir que L'Exception interrupted devrait être une exception RuntimeException elle-même, car cela encouragerait une meilleure gestion "par défaut". Ce n'est pas une RuntimeException uniquement parce que les concepteurs ont maintenu une règle catégorique selon laquelle une RuntimeException devrait représenter une erreur dans votre code. Puisqu'une exception InterruptedException ne provient pas directement d'une erreur dans votre code, ce n'est pas le cas. Mais la réalité est que souvent une Exceptioninterretedexception se produit parce qu'il y a une erreur dans votre code, (c.-à-d. boucle sans fin, dead-lock), et L'interruption est une méthode d'un autre thread pour traiter cette erreur.
Si vous savez qu'il y a un nettoyage rationnel à faire, alors faites-le. Si vous connaissez une cause plus profonde de l'Interruption, vous pouvez prendre plus complète de la manipulation.
donc, en résumé, vos choix de manipulation doivent suivre cette liste:
- par défaut, Ajouter aux lancements.
- Si pas autorisé à ajouter à jette, jeter RuntimeException(e). (meilleur choix de plusieurs mauvaises options)
- Seulement quand vous connaissez une cause explicite de L'interruption, manipulez comme désiré. Si votre manipulation est locale à votre méthode, puis réinitialiser interrompu par un appel à Thread.currentThread ().interrompre.)(
je voulais juste ajouter une dernière option à ce que la plupart des gens et des articles mentionnent. Comme l'a déclaré mR_fr0g, il est important de gérer l'interruption correctement soit par:
-
propagation de L'InterruptException
-
restaurer L'État D'interruption sur le fil
ou plus:
- traitement personnalisé de L'interruption
il n'y a rien de mal à gérer l'interruption d'une manière personnalisée en fonction de vos circonstances. Comme une interruption est une demande de résiliation, par opposition à une commande énergique, il est parfaitement valide d'effectuer du travail supplémentaire pour permettre à l'application de traiter la demande avec élégance. Par exemple, si un Thread est endormi, en attente D'une réponse IO ou matérielle, quand il reçoit L'interruption, alors il est parfaitement valide de fermer gracieusement toute connections avant la fin du thread.
je recommande vivement de comprendre le sujet, mais cet article est une bonne source d'information: http://www.ibm.com/developerworks/java/library/j-jtp05236 /
je dirais que dans certains cas, c'est normal de ne rien faire. Probablement pas quelque chose que vous devriez faire par défaut, mais au cas où il n'y aurait aucun moyen pour que l'interruption se produise, Je ne sais pas quoi faire d'autre (probablement erreur de journalisation, mais cela n'affecte pas le flux du programme).
un cas serait dans le cas où vous avez une tâche (blocage) queue. Dans le cas où vous avez un Thread daemon qui gère ces tâches et que vous n'interrompez pas le Thread par vous-même (à ma connaissance la jvm ne ne pas interrompre les threads de démon sur JVM shutdown), Je ne vois aucun moyen pour que l'interruption se produise, et donc il pourrait être simplement ignoré. (Je sais qu'un fil de démon peut être tué par la jvm à tout moment et qu'il n'est donc pas approprié dans certains cas).
EDIT: Un autre cas pourrait être des blocs gardés, au moins basé sur le tutoriel D'Oracle à: http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html