US

Arrêter un thread

Quand est-ce-qu'un thread s'arrête-t-il de s'exécuter ? Quand passe-t-il dans l'état Dead ? Comme on l'a vu dans le premier chapitre, un thread meurt lorsqu'il a fini d'exécuter sa méthode run. Maintenant, il existe des situations dans lesquelles on souhaite pouvoir demander à un thread de s'arrêter prématurément.

Prenons par exemple un thread dont le but est de faire un gros calcul lourd et qui peut prendre du temps. Ce thread va rester en vie et traiter toutes les demandes qu'on lui fera, tant qu'on ne lui indiquera pas qu'il peut arrêter de s'exécuter.

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
public class TaskExecutor implements Runnable
{
    public void run()
    {
        while (true)
        {
            // Do task
        }
    }
}
Listing 3.1 Exemple d'une tâche qui s'exécute indéfiniment.

Méthodes stop et destroy

Si on regarde un peu les méthodes disponibles dans la classe Thread, on peut voir deux méthodes qui peuvent être utilisées pour arrêter un thread : stop et destroy. Celles-ci permettent respectivement d'arrêter et de détruire un thread.

1 
2 
3 
4 
5 
public static void main (String[] args)
{
    Thread thread = new Thread (new TaskExecutor());
    thread.stop();
}
Listing 3.2 Arrêter un thread avec la méthode stop.

Dans cet exemple, on crée donc un nouveau thread qui va exécuter la tâche définie par la classe TaskExecutor. Ensuite, on arrête le thread en appelant la méthode stop. Ce n'est pas une bonne manière de procéder car les deux méthodes stop et destroy sont dépréciées. Cela est dû au fait qu'utiliser ces méthodes n'est pas sûr car cela peut conduire à des situations inconsistantes, voire causer des deadlocks. Vous trouverez une information à propos de cela sur le site de sun.

Utiliser un flag booléen

Une solution pour résoudre ce problème consiste à utiliser un flag booléen comme condition d'arrêt de la boucle while et d'ajouter une méthode permettant de changer la valeur de ce flag.

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
public class FlagCancellableTask implements Runnable
{
    private volatile boolean cancelled;
 
    public void run()
    {
        while (! cancelled)
        {
            // Do task
        }
    }
 
    public void cancel()
    {
        cancelled = true;
    }
}
Listing 3.3 Tâche annulable via un flag booléen.

La boucle va donc continuer à s'exécuter tant que la variable cancelled vaut false (sa valeur initiale). On va pouvoir changer la valeur de cette variable en appelant la méthode cancel, ce qui aura donc pour effet d'arrêter la boucle et par conséquent de terminer l'exécution du thread.

1 
2 
3 
4 
5 
6 
public static void main (String[] args)
{
    TaskExecutor task = new TaskExecutor();
    Thread thread = new Thread (task);
    task.cancel();
}
Listing 3.4 Arrêter un thread annulable via un flag booléen.

Variable volatile

Vous aurez sans doute remarqué que la variable cancelled a été déclarée volatile. Il s'agit de la première fois qu'on se retrouve confronté à ce mot réservé. En déclarant une variable ainsi, on a la garantie que si un thread modifie sa valeur, cette modification sera directement visible par les autres threads. En effet, il se peut que certaines variables soit stockées dans la cache d'un thread (notamment pour les systèmes multi-processeurs) et qu'une modification sur ces variables ne soit pas directement propagée aux autres threads.

Problème avec le flag booléen

L'approche par flag booléen convient pour la plupart des situations. Néanmoins, elle n'est pas toujours valable comme on va le voir sur base de l'exemple suivant. Il s'agit d'une tâche qui va répéter de gros calculs, mais en faisant une pause de 2 secondes entre chaque calcul.

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
public class TimedTask implements Runnable
{
    private volatile boolean cancelled;
 
    public void run()
    {
        while (! cancelled)
        {
            // Do task
 
            try
            {
                Thread.sleep (2000);
            }
            catch (InterruptedException exception){}
        }
    }
 
    public void cancel()
    {
        cancelled = true;
    }
}
Listing 3.5 Tâche temporisée annulable.

Où est le problème avec cette politique d'annulation de tâche ? Supposons que le thread vient d'exécuter la méthode sleep. Il est donc maintenant dans l'état Waiting et ne se remettra à s'exécuter que dans, au minimum, deux secondes. Si entretemps un autre thread a appelé la méthode cancel, l'arrêt du thread ne sera pas immédiat car il faudra attendre qu'il repasse dans l'état Running afin de réévaluer la condition de la boucle while et pouvoir s'arrêter.

Interruption

Pour résoudre le problème qui vient d'être évoqué, on peut utiliser le mécanisme d'interruption. Chaque thread possède un statut d'interruption qui indique si une demande d'interruption sur ce thread a été faite ou non. On peut connaitre ce statut via les méthodes isInterrupted ou interrupted. On peut demander l'interruption d'un thread via la méthode interrupt.

public void interrupt();
public boolean isInterrupted();
public static boolean interrupted();

Avec cela, on va maintenant pouvoir définir une nouvelle manière d'arrêter un thread, même pour la situation évoquée à la fin de la section précédente. Pour cela, on va utiliser comme condition d'arrêt de la boucle, le statut d'interruption. L'avantage d'utiliser ce mécanisme est que, lorsqu'un thread est en attente et qu'on demande son interruption, il va automatiquement sortir de cet état et une exception de type InterruptedException sera produite.

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
public class InterruptionCancellableTask implements Runnable
{
    private volatile Thread currentThread = null;
 
    public void run()
    {
        currentThread = Thread.currentThread();
 
        try
        {
            while (! currentThread.isInterrupted())
            {
                // Do task
 
                Thread.sleep (2000);
            }
        }
        catch (InterruptedException exception){}
    }
 
    public void cancel()
    {
        currentThread.interrupt();
    }
}
Listing 3.6 Tâche annulable via le mécanisme d'interruption.

La première chose qu'on fait est de stocker une instance du thread lié à cette tâche dans la variable d'instance currentThread. On fait cela afin d'avoir une référence vers le thread qu'il faut annuler pour pouvoir appeler la méthode interrupt. Le thread peut maintenant se voir arrêter dans deux situations. Soit il était en train d'exécuter la tâche et c'est via la condition de la boucle qu'il sera arrêté, soit il était dans l'état Waiting et dans ce cas, une exception de type InterruptedException est levée et le thread s'arrête.

Il reste néanmoins un problème avec cette solution. En effet, si on ne crée qu'une seule instance de la classe InterruptionCancellableTask et qu'on lance plusieurs threads avec cette instance comme tâche à exécuter, on ne saura annuler l'exécution que du dernier thread lancé. On verra dans les exercices comment résoudre ce problème.

  • Espace membre
  • Learning Center
  • Les forums
  • Livre d'or
  • Imprimer
  • Boutique
  • Info
  • Règlement
  • Erreur
  • Newsletter

MyPagerank.Net

Firefox 3.6

Browse Happy logo

Open Clip Art Library

Join our Facebook Group

Twitter

Copyright © 2000-2012 UKO. Toute reproduction strictement interdite sans autorisation du webmaster

Valid XHTML 1.1 !
Valid CSS2 !
Level Triple-A conformance icon, W3C-WAI Web Content Accessibility Guidelines 1.0
ICRA Internet Content Rating Association
Creative Commons License
Site optimisé pour Firefox avec une résolution 1024x768 --- Page chargée en 0.0805800 secondes --- This site uses Thumbshots previews