web-dev-qa-db-fra.com

Gestion d'InterruptedException dans Java

Quelle est la différence entre les méthodes de traitement suivantes InterruptedException? Quelle est la meilleure façon de le faire?

try{
 //...
} catch(InterruptedException e) { 
   Thread.currentThread().interrupt(); 
}

OR

try{
 //...
} catch(InterruptedException e) {
   throw new RuntimeException(e);
}

EDIT: J'aimerais aussi savoir dans quels scénarios ces deux types sont-ils utilisés.

269
devnull

Quelle est la différence entre les méthodes suivantes de gestion d'InterruptedException? Quelle est la meilleure façon de le faire?

Vous êtes probablement venu 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 le résultat possible de l’appel de la méthode que vous appelez. Commencez donc par considérer le fait que 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 votre méthode ? Vous pouvez trouver la réponse en réfléchissant aux points suivants:

Cela a-t-il un sens pour la méthode que vous implémentez pour lancer un InterruptedException? Autrement dit, est un InterruptedException un résultat logique lorsque vous appelez votre méthode ?

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

    Exemple : votre méthode attend qu'une valeur du réseau termine le calcul et renvoie un résultat. Si l'appel réseau bloquant émet un InterruptedException, votre méthode ne peut pas terminer le calcul normalement. 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 no , vous ne devez pas déclarer votre méthode avec throws InterruptedException et vous devez (doit!) Intercepter l'exception. Il est important de garder à l’esprit deux choses dans cette situation:

    1. Quelqu'un a interrompu votre discussion. Ce quelqu'un est probablement désireux d'annuler l'opération, de terminer le programme avec grâce, ou autre chose. Vous devriez être poli avec cette personne et revenir de votre méthode sans plus tarder.

    2. Même si votre méthode parvient à produire une valeur de retour raisonnable dans le cas d'un 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é par le point de savoir si une interruption s'est produite pendant l'exécution de votre méthode. Vous devriez donc enregistrer le fait qu'une interruption a eu lieu en activant l'indicateur interrompu: Thread.currentThread().interrupt()

    Exemple : l'utilisateur a demandé d'imprimer une somme de deux valeurs. Imprimer "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 due à un InterruptedException). En d'autres termes, il n'est pas logique de déclarer cette méthode avec throws 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");
         }
    }
    

À présent, il devrait être clair que le simple fait de 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) peut être perdue.

Autres exemples:

Implémentation de Runnable: Comme vous l'avez peut-être découvert, la signature de Runnable.run ne permet pas de réécrire InterruptedExceptions. Eh bien, vous avez souscrit à la mise en œuvre de Runnable, ce qui signifie que vous vous avez souscrit pour traiter un éventuel InterruptedExceptions. Choisissez une interface différente, telle que Callable , ou suivez la deuxième approche ci-dessus.

Appel de Thread.sleep: Vous essayez de lire un fichier et la spécification indique que vous devez essayer 10 fois avec une seconde entre les deux. Vous appelez Thread.sleep(1000). Donc, vous devez vous occuper de 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 consistant à essayer de lire le fichier" . En d’autres termes, il est parfaitement logique que la méthode jette 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 sous forme d'article ici .

383
aioobe

Il se trouve que je venais de lire à ce sujet ce matin alors que je me rendais au travail dans Java Concurrency In Practice de Brian Goetz. En gros, il dit que vous devriez faire l'une des deux choses suivantes:

  1. Propagez le InterruptedException - Déclarez votre méthode pour lancer le InterruptedException vérifié afin que votre appelant puisse s'en occuper.

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

87
mR_fr0g

Qu'essayez-vous de faire?

La InterruptedException est renvoyée lorsqu'un thread est en attente ou en veille et qu'un autre thread l'interrompt à l'aide de la méthode interrupt de 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 le statut "interrompu" du thread depuis un autre endroit.

En ce qui concerne votre autre option de lancer une RuntimeException, cela ne semble pas une très bonne chose à faire (qui va l'attraper? Comment va-t-il être géré?) Mais il est difficile d'en dire plus sans informations supplémentaires.

17
Grodriguez

Pour moi, l’essentiel à cet égard est le suivant: une exception InterruptedException n’est pas un problème, elle est le fil qui fait ce que vous lui avez demandé de faire. Par conséquent, renvoyer le message encapsulé dans une exception RuntimeException n’a aucun sens.

Dans de nombreux cas, il est logique de réexaminer une exception encapsulée dans une exception RuntimeException lorsque vous dites, je ne sais pas ce qui ne va pas ici et je ne peux rien faire pour y remédier, je veux simplement qu'elle sorte du flux de traitement actuel. et cliquez sur le gestionnaire d'exceptions d'application que j'ai pour le connecter. Ce n'est pas le cas avec une exception InterruptedException, c'est simplement le thread qui répond à l'appel de interruption (), il déclenche l'InterruptedException afin d'aider à annuler le traitement du thread de manière opportune.

Alors propagez l'InterruptedException, ou mangez-le intelligemment (c'est-à-dire à un endroit où il aura accompli ce qu'il était censé faire) et réinitialisez l'indicateur d'interruption. Notez que l'indicateur d'interruption est effacé lorsque l'exception InterruptedException est levée; Les développeurs de la bibliothèque Jdk partent du principe que le fait de capturer l'exception revient à le gérer. Par conséquent, l'indicateur est désactivé par défaut.

Donc, définitivement, le premier moyen est meilleur, le deuxième exemple posté dans la question n'est pas utile, sauf si vous ne vous attendez pas à ce que le fil soit réellement interrompu, et l'interrompre revient à une erreur.

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

12
Nathan Hughes

Le bon choix par défaut est d’ajouter InterruptedException à votre liste de projections. 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. Par conséquent, si vous n’avez pas de connaissances supplémentaires, vous devez supposer qu’il s’agit d’un arrêt convivial, et que tout ce qui évite cet arrêt est une réponse qui ne l’est pas.

Java ne lancera pas au hasard les InterruptedException, tous les conseils n'affecteront pas votre application, mais j'ai rencontré un cas où le développeur a suivi la stratégie "avaler" qui est devenue très gênante. Une équipe avait mis au point un grand nombre de tests et utilisé beaucoup Thread.Sleep. Maintenant, nous avons commencé à exécuter les tests sur notre serveur CI, et parfois, en raison de défauts dans le code, nous restions coincés dans des attentes permanentes. Pour aggraver la situation, lorsqu’il tentait d’annuler le travail de CI, celui-ci ne se fermait jamais car le thread.Interrupt destiné à abandonner le test n’avait pas abandonné le travail. Nous avons dû nous connecter à la boîte et tuer manuellement les processus.

Bref, si vous lancez simplement l'exception InterruptedException, vous faites correspondre l'intention par défaut à laquelle votre thread devrait se terminer. Si vous ne pouvez pas ajouter InterruptedException à votre liste de projection, vous devez l'envelopper dans une exception RuntimeException.

Il existe un argument très rationnel selon lequel InterruptedException devrait être une exception RuntimeException elle-même, car cela encouragerait une meilleure gestion des "défauts". Il ne s’agit pas d’une exception RuntimeException uniquement parce que les concepteurs s’en tenaient à une règle catégorique selon laquelle une exception RuntimeException devait représenter une erreur dans votre code. Etant donné qu'une exception InterruptedException ne découle pas directement d'une erreur dans votre code, ce n'est pas le cas. Mais la réalité est qu’une exception InterruptedException se produit souvent parce qu’il existe une erreur dans votre code (c’est-à-dire une boucle sans fin, un blocage bloqué) et que l’interruption est une méthode d’un autre thread permettant de traiter cette erreur.

Si vous savez qu'il y a un nettoyage rationnel à faire, faites-le. Si vous connaissez une cause plus profonde de l’interruption, vous pouvez prendre en charge un traitement plus complet.

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

  1. Par défaut, ajouter aux lancers.
  2. S'il n'est pas autorisé à ajouter des jets, lancez RuntimeException (e). (Meilleur choix de plusieurs mauvaises options)
  3. Lorsque vous connaissez une cause explicite de l'interruption, gérez-la comme vous le souhaitez. Si votre traitement est local à votre méthode, réinitialisez alors, interrompu par un appel à Thread.currentThread (). Interrupt ().
9
nedruod

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

  • Propagation de l'InterruptException

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

Ou en plus:

  • Traitement personnalisé de l'interruption

Il n’ya rien de mal à traiter l’interruption de manière personnalisée en fonction de votre situation. Comme une interruption est une demande de fin, par opposition à une commande forcée, il est parfaitement correct 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 et attend IO ou une réponse matérielle lors de la réception de l'interruption, il est alors parfaitement correct de fermer correctement toutes les connexions avant de mettre fin au 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/

3
TheIT

Je dirais que dans certains cas, il est possible de ne rien faire. Ce n'est probablement pas quelque chose que vous devriez faire par défaut, mais au cas où il n'y aurait aucun moyen d'interrompre, je ne sais pas quoi faire d'autre (probablement une erreur de journalisation, mais cela n'affecte pas le déroulement du programme).

Un cas serait dans le cas où vous avez une file d'attente de tâches (de blocage). Si vous avez un thread démon qui gère ces tâches et que vous n'interrompez pas le thread par vous-même (à ma connaissance, le jvm n'interrompt pas les threads du démon lors de l'arrêt de jvm), je ne vois aucun moyen que l'interruption se produise et juste ignoré. (Je sais qu'un thread de démon peut être tué par le JVM à tout moment et ne convient donc pas dans certains cas).

EDIT: Un autre cas pourrait être les blocs gardés, du moins basés sur le tutoriel d’Oracle à: http://docs.Oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

0
Bjarne Boström