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.
En pratique
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.
-
public class ThreadInterruption
-
{
-
public static void main(String[] args) throws Exception
-
{
-
// Démarrage du thread
-
CounterThread counter = new CounterThread();
-
counter.start();
-
// Attente d’une interruption manuelle
-
System.in.read();
-
System.out.println(“Interruption !”);
-
counter.interrupt();
-
// Attente de la fin du thread
-
counter.join();
-
}
-
public static class CounterThread extends Thread
-
{
-
@Override
-
public void run()
-
{
-
// Tant que le thread n’est pas interrompu…
-
long time = 0L;
-
while (! isInterrupted())
-
{
-
time = System.currentTimeMillis();
-
try
-
{
-
// Une seconde d’attente
-
Thread.sleep(1000);
-
}
-
catch (InterruptedException e)
-
{
-
System.out.println(“J’ai été interrompu !”);
-
// Activation du flag d’interruption
-
Thread.currentThread().interrupt();
-
}
-
System.out.println(“Boucle de “ + (System.currentTimeMillis() – time) + “ms”);
-
}
-
}
-
}
-
}
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