11. Processus légers (threads)

Java permet l'exécution concurrente de plusieurs processus légers threads, c'est-à-dire plusieurs tâches. On différencie généralement les processus légers des processus lourds. Les premiers sont internes à une application alors que les processus lourds sont vus par le système d'exploitation comme plusieurs processus. Les processus légers partagent un espace d'adressage commun. Comme on le sait, programmation concurrente sous-entend mécanismes de synchronisation et d'exclusion mutuelle.

Le mécanisme d'exclusion mutuelle présent dans l'environnement Java est le moniteur, que l'on implémente grâce au modificateur synchronized. Le principe de base est qu'un seul processus ne peut entrer dans un moniteur si celui-ci est occupé.

Mais voyons tout d'abord comment créer plusieurs processus. Nous pouvons tout simplement étendre la classe java.lang.Thread et remplacer sa méthode run() par notre propre algorithme. Lorsque l'on appelle la méthode start(), la méthode run() est déclenchée, et elle s'arrête lors d'un appel à stop().

Exemple

class afficheur extends Thread {

    /** constructeur permettant de nommer le processus */
    public afficheur(String s) {
        super(s);
    }

    /** affiche son nom puis passe le controle au suivant */
    public void run() {
        while (true) {
            System.out.println("je suis le processus "+getName());
            Thread.yield();    // passe le controle
        }
    }
    
}

class thread12 {

    public static void main(String args[]) {
    
        afficheur thread1 = new afficheur("1");
        afficheur thread2 = new afficheur("2");
        
        thread1.start();
        thread2.start();
        
        while (true) {
            System.out.println("je suis la tache principale !");
            Thread.yield();
        }
    }

}

Exécution

je suis la tache principale !
je suis le processus 1
je suis le processus 2
je suis la tache principale !
je suis le processus 1
je suis le processus 2
je suis la tache principale !
je suis le processus 1
je suis le processus 2
je suis la tache principale !
je suis le processus 1
je suis le processus 2
je suis la tache principale !
je suis le processus 1
je suis le processus 2
je suis la tache principale !
je suis le processus 1
je suis le processus 2
je suis la tache principale !
je suis le processus 1
je suis le processus 2
je suis la tache principale !
je suis le processus 1
je suis le processus 2
Le problème est que la méthode Thread.stop() est qualifiée de final. On ne peut donc pas la remplacer.

Une autre solution est possible. L'interface Runnable permet de créer un objet, que l'on utilisera ensuite comme constructeur d'un Thread. C'est la méthode start() de l'objet qui sera responsable de la création et de l'activation du thread.

Exemple

class afficheurRunnable implements Runnable {

    boolean cont = true;
    String nomProcessus;
    Thread th;
    
    /** constructeur permettant de nommer le processus */
    public afficheurRunnable(String s) {
        nomProcessus = s;
    }

    public void start() {
        if (th == null) {
            th = new Thread(this,nomProcessus);
            th.start();
        }
    }
    
    public void stop() {
        if (th != null) {
            th.stop();
            th = null;
        }
    }

    /** affiche son nom puis passe le controle au suivant */
    public void run() {
        while (cont) {
            System.out.println("je suis le processus "+th.getName());
            Thread.yield();    // passe le controle
        }
    }
    
}

class runnable12 {

    public static void main(String args[]) {
    
        afficheurRunnable run1 = new afficheurRunnable("1");
        afficheurRunnable run2 = new afficheurRunnable("2");
        
        run1.start();
        run2.start();
        
        while (true) {
            System.out.println("je suis la tache principale !");
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {}
        }
    }

}

Exécution

je suis la tache principale !
je suis le processus 1
je suis le processus 2
je suis le processus 1
je suis le processus 2
je suis le processus 1
je suis le processus 2
je suis le processus 1
je suis le processus 2
je suis la tache principale !
je suis le processus 1
je suis le processus 2
je suis le processus 1
je suis le processus 2
je suis le processus 1

Attention !

L'implémentation et le comportement de l'ordonnanceur de processus (scheduler) n'est pas spécifié par Sun. Cela veut dire que les différentes machines virtuelles (MV) Java n'auront pas forcément le même comportement. Une MV peut se comporter comme un noyau temps réel (pas de timeslicing) ou comme un noyau préemptif.

Comme nous l'avons vu dans les exemples précédents, on utilise Thread.yield() afin de redonner le contrôle à l'ordonnanceur. De cette manière, quel que soit le comportement de la machine virtuelle Java, les différents processus s'entrelaceront.

Il est également important de s'assurer que les accès concurrents aux variables et méthodes ne mettent pas en péril la validité des données. Pour ce faire, veuillez vous reporter à la page décrivant les mécanismes d'exclusion mutuelle de Java.

Pour les mécanismes de synchronisation inter-processus, veuillez vous référer à la page décrivant les signaux.


Index général - Index concepts - Règles BNF
© 1996, DIP Genève, Alexandre Maret & Jacques Guyot
page générée Fri Jun 21 15:41:27 MET DST 1996