web-dev-qa-db-fra.com

Est-il OK d'ignorer InterruptedException si personne n'appelle interrupt ()?

Si je crée mon propre thread (c'est-à-dire pas un pool de threads) et que j'appelle quelque part sleep ou toute autre méthode interruptible, est-ce correct d'ignorer l'Exception Interrompue si je sais que personne d'autre dans le code ne fait un interrompre sur le fil.

En d'autres termes, si le thread est censé vivre aussi longtemps que la JVM, ce qui signifie que le thread n'est pas interruptible, est-il sûr de supposer que InterruptedException ne jamais être appelé et donc l'exception peut être avalée?

41
Hilikus

Ignorer une exception vérifiée n'est jamais considéré comme sûr.
Cela peut vous convenir pour le moment, mais si un autre programmeur étend votre code, il attendra le comportement standard: le thread réagissant à un appel d'interruption.
Un bloc de capture vide est également dangereux dans ce cas, car la JVM supprime l'indicateur interrompu et il doit certainement être remis à zéro avec

Thread.currentThread().interrupt();

dans le bloc de capture. À mon avis, il s'agit de l'implémentation de capture minimale pour InterruptedExceptions. La vérification du drapeau isInterrupted dans une boucle ne fait pas trop mal non plus.
.

Si vous pensez que la lisibilité de votre code souffre de ces implémentations catch, vous pouvez implémenter votre propre méthode utilitaire safeSleep, qui prend soin des Exceptions et définit correctement l'indicateur.

En revanche, InterruptedException n'est pas lancé par la JVM elle-même en cas de panne matérielle, c'est un utilisateur indiqué Exception uniquement Donc, si vous ne propagez pas votre référence Threads, il n'y aura pas d'autres Threads capables d'appeler Thread.interrupt() dessus. C'est ça techniquement. Mais vous ne devez pas sous-estimer le facteur humain et l'évolution de vos programmes.
Edit: Comme ruakh l'a souligné, il existe en fait un moyen d'obtenir un Threads référence et donc de planifier un appel Thread.interrupt(). De cette façon, le développeur en chaleur peut même ne pas avoir à regarder la classe, qui implémente votre Thread sans interruption. À mon avis, c'est encore une autre raison, pour implémenter une gestion des exceptions appropriée.
Autre chose : Si vous ne lancez pas un Exception, la journalisation d'un tel événement à un niveau supérieur à INFO peut être un bon choix.

34
Zhedar

Au lieu de l'avaler, si vous êtes si sûr que cela n'arrivera jamais, vous pouvez planter au lieu de l'ignorer. Par exemple, lancez un Error (ou un RuntimeException):

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    throw new Error(e);
}

De les Javadocs pour l'erreur :

Une erreur est une sous-classe de Throwable qui indique de graves problèmes qu'une application raisonnable ne devrait pas essayer d'attraper. La plupart de ces erreurs sont des conditions anormales.

Si vous pensez que cela vaut la peine de supposer que quelque chose ne se produira jamais, alors si cela se produit, c'est une "condition anormale" qui, qui sait, pourrait bien être un "problème grave".

Une autre façon de voir les choses est la suivante: si quelqu'un à l'avenir interrompt votre méthode, y a-t-il une chance qu'il veuille qu'elle continue de fonctionner comme si rien ne s'était passé? Je suppose que non.

33
Dan Getz

Vous ne devez jamais avaler une exception si vous pensez qu'elle ne devrait jamais se produire. Il est normal de décider de ne pas ajouter de code gérant une condition qui ne se produit jamais, mais cela ne devrait pas se produire en silence.

Le minimum que vous devez faire est:

catch(InterruptedException ex) {
  throw new AssertionError(ex);
}

Cela garantit que chaque fois que vous affirmerez que cela ne se produira jamais est faux, vous le remarquerez. Ce modèle s'applique également à d'autres exceptions inattendues, par exemple IOException lorsque vous savez que la cible OutputStream est un ByteArrayOutputStream ou que le Appendable d'un Formatter doit être un StringBuilder, etc.


Soit dit en passant, il existe une alternative à Thread.sleep ne nécessitant pas du tout de traiter avec InterruptedException:

LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(timeInMillis));

Cette méthode retournera simplement plus tôt lorsque le thread a été interrompu et conservera l'état interrompu de Thread afin de fournir déjà une gestion correcte pour le cas où votre code est utilisé par quelqu'un d'autre qui le fait utiliser interruption (en supposant que l'appelant de votre code vérifie alors l'état interrompu).

17
Holger

Le livre que je considère toujours comme la Bible sur ces questions, Goetz et al. La concurrence Java en pratique dit les bits importants suivants dans le chapitre 7 (À peu près le même matériel se trouve dans Goetz article developerWorks , ce qui a l'avantage d'être gratuit, mais le livre est un peu plus clair sur certains points.)

L'interruption est un mécanisme coopératif. Un thread ne peut pas forcer un autre à arrêter ce qu'il fait et à faire autre chose; lorsque le thread A interrompt le thread B, A demande simplement que B arrête ce qu'il fait quand il arrive à un point d'arrêt convenable - s'il en a envie.

[...]

Seul le code qui implémente la stratégie d'interruption d'un thread peut avaler une demande d'interruption. La tâche à usage général et le code de bibliothèque ne doivent jamais avaler les demandes d'interruption.

Donc, oui, vous pouvez "avaler" InterruptedException dans les circonstances que vous avez décrites.

Également un point important dans le livre:

Étant donné que chaque thread a sa propre stratégie d'interruption, vous ne devez pas interrompre un thread à moins de savoir ce que signifie l'interruption pour ce thread.

Vous pouvez donc choisir votre propre stratégie comme "planter" avec un RuntimeException, AssertionError, en le journalisant simplement comme un avertissement, ou en ignorant complètement l'interruption tant que vous documentez ce problème pour quiconque d'autre pourrait avoir besoin à savoir. Ce qui est le mieux dépend de ce que fait réellement votre thread et vous n'avez fourni aucun détail à ce sujet. Par exemple, s'il fonctionne sur une fusée [disons faire le contrôle d'attitude], vous ne voulez pas planter la JVM/la fusée simplement parce qu'un autre programmeur [qui pourrait même ne pas travailler pour la même entreprise que vous, par exemple il a écrit une bibliothèque que vous appelez] a oublié un certain contrat quelque part dans son code et vide une exception en toute sécurité ignorable sur la vôtre: "L'exception qui s'est produite n'était pas due à un échec aléatoire mais à une erreur de conception."

10
Fizz

Si votre Projekt grandit et devient plus complexe, il devient de plus en plus difficile de dire que personne n'appellera certainement la méthode d'interruption. Si quelqu'un appelle cette méthode un jour et qu'une exception InterruptedException se produit, il devient très difficile de la déboguer si vous ne la connectez pas quelque part.

7
Jan

Il ne faut pas l'ignorer. Il doit être renvoyé à l'appelant ou vous devez réaffirmer l'état interrompu du thread qui est effacé lorsque l'exception est levée:

catch (InterruptedException e) { 
     // Restore the interrupted status
     Thread.currentThread().interrupt();
 }

à moins que vous ne quittiez le thread après la capture.

Ici est un article sur le traitement des InterruptedExceptions.

7
M Anouti

si le thread est censé vivre aussi longtemps que la JVM, ce qui signifie qu'il n'est pas interruptible, est-il sûr de supposer que InterruptedException ne sera jamais être levé et donc l'exception peut être avalée?

Je comprends votre douleur, la prolifération de blocs de reprise d'essai sans valeur commerciale nuit au code.

Si le code où vous avez l'intention d'avaler l'exception est entièrement sous votre contrôle, et si c'est juste le code client (il ne sera jamais réutilisé dans un contexte autre que le vôtre), alors il est prudent d'avaler l'exception. Notez qu'il y a beaucoup de si il y en a.

S'il vous arrive de travailler avec Java 8, vous avez une autre option, décrite ici : enveloppez votre code dans un bloc uncheckCall(() -> { ... }). Cela permet au le bloc de code jette le InterruptedException sans le déclarer. Comme expliqué dans la réponse liée, cela doit être manipulé avec un soin extrême, mais il a le potentiel de rendre votre code assez propre.

2
Marko Topolnik

Je pense qu'ignorer InterruptedException est sûr ou non, dépend de ce que vous faites dans ce fil. S'il y a un scénario où une interruption indésirable de votre thread peut laisser le système ou toute ressource dans un état indésirable (sale, verrouillé, non libéré, ce qui peut entraîner une fuite de ressource), il est de votre responsabilité de gérer l'exception InterruptedException et vous ne devez pas l'ignorer. C'est à vous de décider si vous voulez que votre fil soit interruptible ou non.

En d'autres termes, le mécanisme d'interruption est principalement utilisé pour mettre en œuvre les politiques d'annulation et le nettoyage des tâches. Si vous n'avez rien à nettoyer dans la tâche ou si vous n'avez aucune politique d'annulation de tâche spécifique, ignorer IntrruptedException peut ne pas sembler politiquement correct, mais c'est OK.

1
Mak