US

Thread

On vient de revoir comment se passe l'exécution d'un programme séquentiel. Dans un tel programme, les instruction s'exécutent les unes à la suite des autres et il n'y en a qu'une seule à tout moment qui est en cours d'exécution. Lorsqu'on veut écrire un programme concurrent, plusieurs alternatives s'offrent à nous.

Application concurrente

Une application concurrente est capable d'effectuer plusieurs tâches en même temps, en parallèle. Si la machine dispose de plusieurs processeurs ou d'un processeur avec plusieurs cœur, les tâches pourront être exécutées réellement en parallèle. Sinon, le parallélisme sera simulé par le système d'exploitation.

La première manière d'écrire une application concurrente consiste à la diviser en plusieurs programmes qui seront tous exécutés en même temps. C'est comme quand vous démarrez plusieurs programmes sur votre ordinateur, ils fonctionnent tous en parallèle et sont capable de communiquer entre eux.

L'autre solution consiste à n'avoir qu'un seul programme, mais qui est constitué de plusieurs threads d'exécution. Il s'agit en fait de tâches dans un programme qui pourront être exécutées en parallèle. C'est précisément cette approche que nous allons voir dans le cadre de ce tutoriel. Java est tout spécialement prévu pour cela et intègre dans le langage les threads d'exécution. Le grand avantage de ces derniers est qu'ils sont beaucoup plus facile à créer et nécessitent moins de ressources étant donné qu'ils vont pouvoir partager les ressources du programme qui les a lancé.

Créer un thread d'exécution

Pour écrire des applications concurrentes en Java, on va utiliser la classe Thread qui se trouve dans le package java.lang. Celle-ci représente deux choses à la fois : la tâche qui doit être exécutée en parallèle et le thread d'exécution (on dira simplement thread à partir de maintenant). Pour définir une nouvelle tâche, il suffit d'écrire une nouvelle classe qui étend la classe Thread et de redéfinir la méthode run. Voici par exemple une tâche toute simple qui est un décompte, à utiliser par exemple lors du nouvel-an ou lors du lancement d'une fusée vers l'espace.

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
public class CountDown extends Thread
{
    public void run()
    {
        for (int i = 5; i >= 0; i--)
        {
            System.out.println (i);
        }
    }
}
Listing 1.2 La classe CountDown : définition d'une tâche.

Jusqu'à présent, rien de bien nouveau. On a juste défini un nouvelle classe qui étend la classe Thread. On peut faire tout ce qu'on a toujours fait avec une telle classe, comme par exemple en créer une nouvelle instance et pourquoi pas appeler la méthode run :

1 
2 
Thread thread = new CountDown();
thread.run();
Listing 1.3 Création d'une instance de la classe CountDown et appel de la méthode run.

Ce programme est complètement séquentiel, il n'y a aucun thread qui a été créé. La tâche définie par la méthode run est bien exécutée, mais pas en parallèle avec le reste du programme. Ce n'est donc pas du tout ce qui nous intéresse, mais il est néanmoins important de mentionner ce fait car il est parfois la source d'erreurs logiques.

Pour créer un nouveau thread et donc avoir la tâche exécutée en parallèle, il faut appeler la méthode start de la classe Thread. Cette méthode fait deux choses : elle crée tout d'abord un nouveau thread d'exécution et elle y exécute ensuite la méthode run.

1 
2 
Thread thread = new CountDown();
thread.start();
Listing 1.4 Création d'un nouveau thread et exécution d'une tâche dans celui-ci.

Cette fois-ci, le décompte va bien s'exécuter dans un nouveau thread, en parallèle avec le reste du programme. À chaque nouveau thread est associée une pile d'environnements. Par contre, il n'y a toujours qu'un unique tas pour stocker tous les objets créés dans tout le programme. La figure suivante vous montre l'état de la mémoire après création du thread et lorsque sa méthode run a déjà commencer à être exécutée.

État de la mémoire après création d'un thread
Figure 1.3 État de la mémoire après création d'un thread.

On peut donc voir sur la figure que dans tout programme, même dans les séquentiels, il y a toujours un thread appelé main-thread. Il s'agit d'un thread qui va exécuter la méthode main du programme. L'environnement de cette méthode contient la variable thread qui contient une référence vers l'objet CountDown. On voit ensuite l'environnement du thread démarré par l'appel de la méthode start sur l'objet référencé par la variable thread. Celui-ci contient la variable i utilisée par la boucle for. Il faut donc bien retenir qu'il y a toujours un unique tas et une pile d'environnements par thread.

Classe Thread

Maintenant qu'on a vu les bases nous permettant de créer des threads, regardons d'un peu plus près quelques méthodes de la classe Thread. On ne va pas toutes les voir tout de suite, il faudra attendre d'avoir vu un peu plus de matière pour cela.

Commençons avec les constructeurs de la classe. Deux nous intéressent à ce stade :

public Thread();
public Thread (String name);

On a déjà utilisé le premier constructeur implicitement, mais le second est assez intéressant. Grâce à ce dernier, on va pouvoir donner un nom au thread. Vous vous demandez surement en quoi cela peut être utile. Si on ne l'utilise pas, les threads sont successivement nommé thread-0, thread-1, ... Lorsqu'une erreur d'exécution se produit, le nom du thread est affiché dans la trace d'erreur, en définir un soi-même peut donc aider pour corriger ces erreurs.

Parmi les méthodes d'instance de la classe, voyons-en trois :

public final String getName();
public final void setName (String name);
public void run();
public void start();

La méthode getName permet de récupérer le nom du thread. On vient de voir qu'on pouvait changer le nom via le constructeur. On peut également utiliser la méthode setName. Rien n'interdit à deux threads d'avoir le même nom. Donc, si vous spécifiez les noms par vous-même, faites attention à cela sans quoi vous pourriez avoir du mal à débugguer vos programmes en cas d'erreur.

On a déjà vu les deux méthodes suivantes. La méthode run contient la tâche à exécuter dans un nouveau thread. Vous ne devriez jamais avoir à l'appeler par vous-même, mais à chaque fois que vous écrivez une nouvelle classe qui étend la classe Thread, vous devez redéfinir cette méthode. Enfin, la méthode start permet de créer un nouveau thread et d'y exécuter la méthode run.

La classe Thread contient également un certain nombre de méthodes de classe. Deux méritent notre attention à ce stade-ci :

public static Thread currentThread();
public static void sleep (long milliseconds) throws InterruptedException;

La méthode currentThread permet de récupérer l'instance du thread qui est en train d'exécuter le code. On peut par exemple l'utiliser dans une méthode main et récupérer le nom du thread qui exécute cette méthode :

1 
2 
3 
4 
5 
public static void main (String[] args)
{
    Thread thread = Thread.currentThread();
    System.out.println (thread.getName());
}
Listing 1.5 Récupérer le nom du thread qui exécute la méthode main.

Si vous exécutez ce programme, il affichera main sur la sortie standard. Vous pouvez facilement écrire un programme qui crée plusieurs threads sans spécifier de noms pour voir ceux choisis automatiquement par Java, je vous laisse faire cela en guise d'exercice.

La méthode sleep quant à elle, permet de suspendre le thread courant durant un certain temps spécifié en millisecondes. Attention, comme on va le voir à la section suivante, le thread sera en fait suspendu durant au minimum le temps spécifié, il ne s'agit pas d'un temps précis. Vous ne devriez donc pas, par exemple, utiliser cette fonctionnalité pour écrire une horloge. L'exemple suivant montre un thread qui va récupérer régulièrement la température d'une ville sur Internet pour l'afficher sur la sortie standard :

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
public class WeatherChecker extends Thread
{
    public void run()
    {
        while (true)
        {
            try
            {
                // Récupère la température de Bruxelles (Belgique)
                String t = getTemperature(968019);
                System.out.println ("Il fait " + t + "°C à Bruxelles.");
 
                Thread.sleep (60000); // Attend 1 minutes
            }
            catch (InterruptedException exception){}
        }
    }
 
    private static String getTemperature (int code) { /* CODE NON-FOURNI */ }
}
Listing 1.6 La classe WeatherChecker : utilisation de la méthode sleep de la classe Thread.

On ne vous a pas mis tout le code ici, vous le trouverez dans les ressources attachées à ce chapitre. La méthode run commence par une boucle infinie avec while (true). À chaque itération de la boucle, on va chercher la température à Bruxelles sur Internet grâce à la méthode getTemperature, ensuite on met le thread en pause pendant une minute (60 000 millisecondes). Durant ce laps de temps, le programme n'utilise aucune ressource processeur, il est en attente. Après au minimum une minute, le thread redeviendra actif et l'itération suivante de la boucle aura donc lieu.

Vous aurez également remarqué que la méthode sleep est susceptible de générer une erreur de type InterruptedException. Nous verrons plus tard dans ce tutoriel à quoi correspond exactement cette erreur. On l'utilisera notamment pour pouvoir arrêter la boucle infinie proprement.

  • 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.0484090 secondes --- This site uses Thumbshots previews