Gestion de l'exception InterruptedException en Java


Quelle est la différence entre les manières 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 également savoir dans quels scénarios ces deux sont-ils utilisés.

Author: Nathan Hughes, 2010-10-20

7 answers

Vous êtes probablement venu poser cette question parce que vous avez appelé une méthode qui lance InterruptedException.

Tout d'abord, vous devriez voir throws InterruptedException pour ce que c'est: Une partie de la signature de la méthode et un résultat possible de l'appel de la méthode que vous appelez. Commencez donc par accepter le fait qu'un InterruptedException est un résultat parfaitement valide de l'appel de méthode.

Maintenant, si la méthode que vous appelez lève une telle exception, que devrait faire votre méthode? Vous pouvez trouver la réponse par penser à ce qui suit:

Cela a-t-il un sens pour la méthode que vous implémentez pour lancer un InterruptedException? Dit différemment, un InterruptedExceptionest-il un résultat raisonnable lors de l'appel à votre méthode?

  • Si oui, alors throws InterruptedExceptiondevrait faire partie de votre signature de méthode, et vous devriez laisser l'exception se propager (c'est-à-dire ne pas l'attraper du tout).

    Exemple: Votre méthode attend pour une valeur de réseau pour terminer le calcul et renvoyer un résultat. Si l'appel réseau bloquant lance un InterruptedException, votre méthode ne peut pas terminer le calcul de manière normale. Vous laissez le InterruptedException 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 devez pas déclarer votre méthode avec throws InterruptedException et vous doit (doivent!) attrapez l'exception. Maintenant, deux choses sont importantes à garder à l'esprit dans cette situation:

    1. Quelqu'un a interrompu votre fil. Que quelqu'un est probablement désireux de annuler l'opération, mettre fin au programme gracieusement, ou quoi que ce soit. Vous devriez être poli avec cette personne et revenir de votre méthode sans plus tarder.

    2. Même si votre méthode peut réussir à produire une valeur de retour raisonnable en cas de InterruptedException, le fait que le thread ait été interrompu peut toujours être important. En particulier, le code qui appelle votre méthode peut être intéressé à savoir si une interruption s'est produite lors de l'exécution de votre méthode. Vous devrait donc enregistrer le fait qu'une interruption a eu lieu en définissant le 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 bien mieux que de laisser le programme planter avec une trace de pile en raison d'un InterruptedException). En d'autres termes, il n'est pas logique que déclare 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 il est clair que le simple fait de faire throw new RuntimeException(e) est une mauvaise idée. Ce n'est pas très poli avec l'appelant. Vous pouvez inventer une nouvelle exception d'exécution, mais la cause première (quelqu'un veut que le thread arrête l'exécution) pourrait être perdue.

D'Autres exemples:

Mise en œuvre Runnable: Comme vous l'avez peut-être découvert, la signature de Runnable.run ne permet pas de repenser InterruptedExceptions. Eh bien, vous avez signé pour mettre en œuvre Runnable, ce qui signifie que vous avez signé pour traiter possible InterruptedExceptions. Choisir une interface différente, comme Callable, ou suivez la deuxième approche ci-dessus.

 

Appel Thread.sleep: Vous essayez de lire un fichier et la spécification dit que vous devriez essayer 10 fois avec 1 seconde entre les deux. Vous appelez Thread.sleep(1000). Donc, vous devez traiter avec InterruptedException. Pour une méthode telle que tryToReadFile, il est parfaitement logique de dire, "Si je suis interrompu, je ne peux pas terminer mon action d'essayer de lire les le fichier". En d'autres termes, il est parfaitement logique pour la méthode de jeter InterruptedExceptions.

String tryToReadFile(File f) throws InterruptedException {
    for (int i = 0; i < 10; i++) {
        if (f.exists())
            return readFile(f);
        Thread.sleep(1000);
    }
    return null;
}

Cet article a été réécrit en tant qu'article ici.

 298
Author: aioobe, 2016-11-28 12:23:39

Comme il se trouve, je lisais juste à ce sujet ce matin sur mon chemin pour travailler dans Java Concurrency In Practice par Brian Goetz. Fondamentalement, il dit que vous devriez faire l'une des deux choses

  1. Propager le InterruptedException - Déclarez votre méthode pour lancer le InterruptedException vérifié afin que votre appelant doive y faire face.

  2. Restaurer l'Interruption - Parfois, vous ne pouvez pas jeter InterruptedException. Dans ces cas, vous devez attraper le InterruptedException et restaurer l'interruption statut en appelant la méthode interrupt() sur le currentThread afin que le code plus haut de la pile d'appels puisse voir qu'une interruption a été émise.

 73
Author: mR_fr0g, 2014-02-24 13:04:54

Qu'essayez-vous de faire?

Le InterruptedException est lancé lorsqu'un thread est en attente ou en sommeil et qu'un autre thread l'interrompt en utilisant la méthode interrupt dans la classe Thread. Donc, si vous attrapez cette exception, cela signifie que le thread a été interrompu. Habituellement, il est inutile d'appeler à nouveau Thread.currentThread().interrupt();, à moins que vous ne souhaitiez vérifier l'état "interrompu" du thread depuis un autre endroit.

En ce qui concerne votre autre option de lancer un RuntimeException, cela ne semble pas une chose très sage à faire (qui le fera attraper cette? comment peut-elle être traitée?) mais il est difficile d'en dire plus sans informations supplémentaires.

 16
Author: Grodriguez, 2010-10-20 09:30:24

Pour moi, la chose clé à ce sujet est: une exception InterruptedException n'est pas quelque chose qui ne va pas, c'est le thread qui fait ce que vous lui avez dit de faire. Par conséquent, le repenser enveloppé dans une RuntimeException n'a aucun sens.

Dans de nombreux cas, il est logique de repenser une exception enveloppée dans une exception RuntimeException lorsque vous dites, je ne sais pas ce qui s'est mal passé ici et je ne peux rien faire pour le réparer, je veux juste qu'il sorte du flux de traitement actuel et frappe n'importe quelle exception à l'échelle de l'application gestionnaire 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'interrupt (), il lance l'InterruptedException afin d'aider à annuler le traitement du thread en temps opportun.

Propagez donc l'exception InterruptedException, ou mangez-la intelligemment (c'est-à-dire à un endroit où elle aura accompli ce qu'elle était censée faire) et réinitialisez l'indicateur d'interruption. Notez que l'indicateur d'interruption est effacé lorsque le InterruptedException est levée; l'hypothèse des développeurs de la bibliothèque Jdk est que la capture de l'exception revient à la gérer, donc par défaut l'indicateur est effacé.

Donc certainement la première façon est meilleure, le deuxième exemple publié dans la question n'est pas utile sauf si vous ne vous attendez pas à ce que le thread soit réellement interrompu, et l'interrompre équivaut à une erreur.

Voici une réponse que j'ai écritedécrivant le fonctionnement des interruptions, avec un exemple . Vous pouvez le voir dans la exemple de code où il utilise l'exception InterruptedException pour renflouer une boucle while dans la méthode Runnable.

 11
Author: Nathan Hughes, 2017-05-23 11:54:40

Le choix par défaut correct est d'ajouter InterruptedException à votre liste de lancers. 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 lancera pas au hasard InterruptedException, tous les conseils n'affecteront pas votre application mais j'ai rencontré un cas où le développeur suivant la stratégie "swallow" est devenu très gênant. Une équipe avait développé un grand ensemble de tests et utilisé Thread.Dormir beaucoup. Maintenant, nous avons commencé à exécuter les tests dans notre serveur CI, et parfois en raison de défauts dans le code serait coincé dans des attentes permanentes. Pour aggraver la situation, lorsque vous essayez d'annuler le travail CI, il ne s'est jamais fermé parce que le Thread.L'interruption qui était destinée à interrompre le test n'a pas interrompu le travail. Nous avons eu pour vous connecter à la boîte et tuer manuellement les processus.

Si longue histoire courte, si vous lancez simplement l'exception InterruptedException, vous correspondez à l'intention par défaut que votre thread devrait se terminer. Si vous ne pouvez pas ajouter InterruptedException à votre liste de lancers, je l'encapsulerais dans une RuntimeException.

Il y a un argument très rationnel à faire selon lequel InterruptedException devrait être une RuntimeException elle-même, car cela encouragerait une meilleure gestion "par défaut". Ce n'est pas une exception RuntimeException uniquement parce que les concepteurs ont respecté une règle catégorique selon laquelle une RuntimeException devrait représenter une erreur dans votre code. Puisqu'une 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 exception InterruptedException se pose parce qu'il y a une erreur dans votre code, (c'est-à-dire une boucle sans fin, dead-lock), et l'interruption est la 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 une manipulation plus complète.

Donc, en résumé, vos choix de manipulation devraient suivre cette liste:

  1. Par défaut, ajouter aux lancers.
  2. Si vous n'êtes pas autorisé à ajouter aux lancers, lancez RuntimeException(e). (Meilleur choix de plusieurs mauvaises options)
  3. Seulement lorsque vous connaissez une cause explicite de l'interruption, manipulez comme vous le souhaitez. Si votre traitement est local à votre méthode, réinitialisez interrompu par un appel à enfiler.currentThread().interrompre().
 6
Author: nedruod, 2013-09-20 21:32:50

Je voulais juste ajouter une dernière option à ce que la plupart des gens et des articles mentionnent. Comme mR_fr0g l'a déclaré, il est important de gérer correctement l'interruption soit par:

  • Propagation de l'exception InterruptException

  • Restaurer l'état d'interruption sur le thread

Ou en plus:

  • Gestion personnalisée de l'interruption

Il n'y a rien de mal à gérer l'interruption de manière personnalisée en fonction de vos circonstances. Comme une interruption est une demande de terminaison, par opposition à une commande forcée, il est parfaitement valide d'effectuer un travail supplémentaire pour permettre à l'application de gérer la demande avec élégance. Par exemple, si un Thread est en veille, en attente d'E / S ou d'une réponse matérielle, lorsqu'il reçoit l'interruption, il est parfaitement valide de fermer gracieusement toutes les connexions avant de terminer le thread.

Je recommande fortement de comprendre le sujet, mais cet article est une bonne source d'information: http://www.ibm.com/developerworks/java/library/j-jtp05236/

 1
Author: TheIT, 2013-12-17 02:38:10

Je dirais que dans certains cas, il est correct de ne rien faire. Probablement pas quelque chose que vous devriez faire par défaut, mais au cas où il ne devrait y avoir 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 file d'attente de tâche (blocage). Dans le cas où vous avez un thread de démon qui gère ces tâches et que vous n'interrompez pas le Thread par vous-même (à ma connaissance, la jvm n'interrompt pas le démon threads sur l'arrêt de la jvm), je ne vois aucun moyen pour que l'interruption se produise, et donc elle pourrait être simplement ignorée. (Je sais qu'un thread de démon peut être tué par la jvm à tout moment et ne convient donc pas dans certains cas).

MODIFIER: Un autre cas pourrait être des blocs gardés, au moins basés sur le tutoriel d'Oracle à: http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

 0
Author: Bjarne Boström, 2015-02-25 14:11:42