Vous aimez ce que vous lisez sur ce blog ?
Envie d'aller plus loin avec véritable formation d'expertise en Java ?
Venez suivre ma formation Masterclasse Expertise Java !

"Même un développeur experimenté a besoin de continuer à apprendre. Et dans cette formation... j'ai appris beaucoup !" - A.G., Java Champion

Sessions intra-entreprises sur demande : contact[at]mokatech.net.
Inscrivez-vous vite !

Gérer proprement les interruptions de threads en Java

Voici la traduction rapide d'un article d'Alex Miller (avec son aimable permission naturellement), qui récapitule les grands principes de gestion de l'interruption des threads.
Un sujet souvent mal maîtrisé, qui pourtant, vous allez le voir, est relativement simple à comprendre.

Gestion des interruptions, par Alex Miller

Un thread Java ne peut pas être interrompu de manière préemptive. En revanche, une application peut appeler sa méthode Thread.interrupt(). Si le thread est en attente d'une opération bloquante à ce moment-là (comme wait, join ou sleep), une InterruptedException est levée. Dans le cas contraire, seul le flag interrupted du thread est activé.

Un thread recevant une InterruptedException a plusieurs options :

  • Gérer lui-même l'interruption. Le traitement typique consiste à vérifier si une condition particulière a été atteinte (si le thread doit s'arrêter ou changer d'état), et à modifier le comportement du thread en conséquence.
  • Propager l'exception. Celle-ci est immédiatement relancée, et sa gestion incombe alors à du code de plus haut niveau.
  • Retarder la gestion de l'exception. Il suffit pour cela d'appeler Thread.currentThread().interrupt() de manière à réactiver le flag d'interruption du thread. Du code de plus haut niveau peut alors vérifier l'état de ce flag et prendre les mesures adéquates.
  • Ignorer l'exception. L'exception est capturée et étouffée silencieusement. Cette solution est à proscrire - à moins naturellement que le bloc catch ne contienne tout le code nécessaire au traitement de l'interruption,auquel cas il ne s'agit que d'un cas particulier de la première option.

Réfléchissez soigneusement à la politique de gestion des interruptions de vos threads, et à leur réaction dans le cas où ils sont interrompus en pleine attente d'une opération bloquante. De manière générale, les frameworks techniques devraient propager les exceptions de manière transparente, et laisser les applications les gérer en accord avec la politique d'interruption de leurs threads.

Notes personnelles

Voici un exemple de code gérant correctement l'interruption.
Le thread lancé effectue des boucles d'une seconde à l'aide de la méthode bloquante sleep(). L'utilisateur peut déclencher l'interruption de ce thread en saisissant quelquechose dans la console, et constater que la boucle s'est terminée prématurément.

  1. public class ThreadInterruption
  2. {
  3. public static void main(String[] args) throws Exception
  4. {
  5. // Démarrage du thread
  6. CounterThread counter = new CounterThread();
  7. counter.start();
  8.  
  9. // Attente d'une interruption manuelle
  10. System.in.read();
  11. System.out.println("Interruption !");
  12. counter.interrupt();
  13.  
  14. // Attente de la fin du thread
  15. counter.join();
  16. }
  17.  
  18. public static class CounterThread extends Thread
  19. {
  20. @Override
  21. public void run()
  22. {
  23. // Tant que le thread n'est pas interrompu...
  24. long time = 0L;
  25. while (! isInterrupted())
  26. {
  27. time = System.currentTimeMillis();
  28. try
  29. {
  30. // Une seconde d'attente
  31. Thread.sleep(1000);
  32. }
  33. catch (InterruptedException e)
  34. {
  35. System.out.println("J'ai été interrompu !");
  36. // Activation du flag d'interruption
  37. Thread.currentThread().interrupt();
  38. }
  39. System.out.println("Boucle de " + (System.currentTimeMillis() - time) + "ms");
  40. }
  41. }
  42. }
  43. }

Résultat :

Début de la boucle, attente prévue 1000ms
Fin de la boucle, attente réelle 1000ms
Début de la boucle, attente prévue 1000ms
Fin de la boucle, attente réelle 1000ms
Début de la boucle, attente prévue 1000ms
Fin de la boucle, attente réelle 1000ms
Début de la boucle, attente prévue 1000ms
<saisie dans la console>
Interruption !
J'ai été interrompu !
Fin de la boucle, attente réelle 479ms

Commentaires

1. Le jeudi 31 mai 2012, 21:47 par vincent

Il n'y aurait pas une erreur ligne 37:
Thread.currentThread().interrupted();
et non:
Thread.currentThread().interrupt();

2. Le jeudi 31 mai 2012, 22:10 par Olivier Croisier

Non, c'est bien interrupt(), voici pourquoi :

Si le thread est interrompu alors qu'il est dans l'état WAITING (pendant sleep()), il est automatiquement remis en mode RUNNABLE et la première action qu'il entreprendra sera de lancer la fameuse InterruptedException.

Le problème, c'est que le fait de capturer l'InterruptedException remet à zéro l'indicateur d'interruption (un flag interne du thread).

Or, on veut que ce flag soit actif pour pouvoir sortir de la boucle "while (! isInterrupted())". Et la méthode interrupt() a justement cet effet.

Mais alors, que fait la méthode interrupted() ?
La méthode interrupted() signale au thread que l'on a bien vu qu'il était interrompu, et rabaisse le flag d'interruption. Ce n'est pas le comportement qui nous intéresse ici.

3. Le dimanche 3 juin 2012, 11:27 par vincent

Oui effectivement, merci pour ta réponse et au passage bravo pour la qualité de tes billets !

4. Le vendredi 17 août 2012, 15:31 par Fred

Bonjour,

la méthode run de mon thread vérifie une file d'attente en base de données, et lance un traitement )qui peut prendre 30 min, 1h, ou même 3h) sur le premier objet de la file d'attente si celle-ci n'est pas vide.

Ce traitement est représenté dans la méthode run par un appel à monObjetDeTraitement.traite(monObjetATraiter);

lorsque j'appelle la méthode interrupt, mon traitement n'est pas arrêté sur le champ, si je suis au milieu d'une boucle qui prend 5 min elle va continuer et le traitement ne s'arrêtera que si j'appelle une méthode d'une classe qui renvoie une ClosedByInterruptException. Dois-je truffer mon code de tests pour savoir si je dois ou non arrêter mon traitement ?

Merci,

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.