Cette page décrit comment interrompre un thread et comment synchroniser des threads.
Cette page donne aussi des pointeurs vers des sites concernant le Multi-threading.
Gérer les interruptions de Thread
Source
Dans cet article, nous allons voir comment gérer de manière correcte l'exception InterruptedException.
Dans le cas d'utilisation de Thread, il est souvent utile de pouvoir stopper l'exécution d'un Thread. Les méthodes stop, start et suspend étant deprecated, la javadoc conseille d'utiliser un boolean pour indiquer si le Thread doit continuer à s'exécuter.
En fait, la classe Thread contient déjà une variable (accessible via la méthode isInterrupted()) indiquant si le Thread doit s'interrompre. La méthode interrupt() permet d'interrompre un Thread.
La classe TestThread1 illustre ce comportement.
L'exécution de cette classe donne quelque chose du genre:
Jusqu'ici ça marche très bien, mais les choses se compliquent lorsque l'on interrompt un Thread qui est en train d'exécuter une méthode bloquante tel que:
join et sleep de la classe Thread,
wait de la classe Object.
Dans ce cas la méthode interrupt ne va pas interrompre le Thread ( isInterrupted() retourne false) mais provoquer l'exception InterruptedException dans le Thread que l'on essaye de stopper.
Voici un exemple:
L'exécution de cette classe produit l'affichage suivant:
Et là, le programme va se mettre à boucler indéfiniment. Ici, on voit bien que le Thread ne s'est pas interromput correctement. C'est pour cela que lorsqu'on catche une InterruptedException, il faut réinterrompre le Thread en appelant de nouveau la méthode interrupt().
Dans le catch de InterruptedException il faut mettre:
Si on relance TestThread2 avec cette modification on obtient:
Et le programme se termine correctement.
En résumé, il ne faut pas oublier de réinterrompre un Thread lorsque l'on catche une InterruptedException. Si vous voulez plus d'infomations sur ce fonctionnement vous pouvez regarder la javadoc de la classe Thread.
Gérer les interruptions de Thread
Synchronisation de threads : CountDownLatch
Une instance de CountDownLatch est initialisée à l'aide d'un compteur. En général, l'utilisation d'une instance de CountDownLatch se fait conjointement avec un pool de threads. Chacun des threads de ce pool peuvent alors décrémenter le compteur associé à cette instance et se mettre en attente. Ce compteur sert alors de verrou. Lorsque le compteur est décrémenté à 0, le verrou est relâché.
Voici un exemple utilisant un verrou qui sert à synchroniser l'exécution de threads en début et fin de traitement :
Voilà le résultat de l'exécution :
28.953 - Thread 8 At run()
28.953 - Thread 9 At run()
29.046 - Thread 10 At run()
29.046 - Thread 10 Do work
29.046 - Thread 9 Do work
29.046 - Thread 8 Do work
29.078 - Thread 10 Wait for end
29.609 - Thread 9 Wait for end
29.781 - Thread 8 Wait for end
29.781 - Thread 10 Done
29.781 - Thread 9 Done
29.781 - Thread 8 Done
Chaque instance de CountDownLatch ne peut être utilisée qu'une seule et unique fois. Il est possible d'instancier un CountDownLatch en spécifiant une sorte de timeout permettant de libérer le verrou après un certain laps de temps.
Néanmoins, pour synchroniser des threads comme cela est fait sur cet exemple, il est préférable d'utiliser une instance de CyclicBarrier.
Synchronisation de threads : CyclicBarrier
Contrairement à une instance de CountDownLatch, une instance de CyclicBarrier peut être utilisée à plusieurs reprise. CyclicBarrier est donc très pratique pour synchroniser des threads à différents points.
L'exemple précédent utilisant CountDownLatch peut donc se simplifier de la manière suivante :
Le résultat est alors identique au précédent.
Une instance de CyclicBarrier peut aussi être associée à un thread. Ce thread sera alors exécuté lorsque le dernier thread du pool attaché à l'instance de CyclicBarrier libérera le verrou.
Voici un exemple d'utilisation de CyclicBarrier illustrant ceci :
Voici le résultat de l'exécution :
Initialization
8 working for 747
9 working for 23
10 working for 300
Completed in 750 ms