CodeGym /Cours /JAVA 25 SELF /Lancement des threads : Thread et Runnable, syntaxe

Lancement des threads : Thread et Runnable, syntaxe

JAVA 25 SELF
Niveau 51 , Leçon 1
Disponible

1. La classe Thread : premier thread en Java

En Java, chaque thread d’exécution est représenté par un objet de la classe Thread. Cette classe est comme un chef d’orchestre : c’est elle qui pilote le démarrage, l’arrêt et le cycle de vie du thread.

Pour démarrer un thread, il faut :

  1. Créer un objet de la classe Thread (ou d’une sous-classe).
  2. Appeler la méthode start() sur cet objet.

Voyons cela en pratique.

Exemple 1. Héritage de Thread

La manière la plus directe de créer un thread consiste à faire hériter votre classe de Thread et à redéfinir sa méthode run(). Tout ce que vous écrirez dans run() s’exécutera dans un thread séparé.

// Thread simple via héritage
public class HelloThread extends Thread {
    @Override
    public void run() {
        System.out.println("Bonjour depuis le thread ! Je m'appelle : " + getName());
    }
}

public class Main {
    public static void main(String[] args) {
        HelloThread thread = new HelloThread(); // on crée un objet-thread
        thread.start(); // on démarre le thread
        System.out.println("Le thread principal termine son travail.");
    }
}

Que se passe-t-il réellement ?
Quand vous appelez thread.start(), Java crée un nouveau thread et lance à l’intérieur de celui-ci la méthode run(). Pendant ce temps, le thread principal (celui qui a commencé avec main) ne bloque pas et continue son exécution en parallèle.

Et l’avertissement principal : ne confondez pas start() avec un appel direct à run(). Si vous écrivez thread.run(), aucun nouveau thread ne sera créé : ce n’est qu’une méthode ordinaire qui s’exécutera dans le thread courant. Le vrai multithreading ne commence qu’avec start().

Qu’est-ce que getName() ?

La méthode getName() renvoie le nom du thread. Par défaut, Java donne aux threads des noms de la forme « Thread-0 », « Thread-1 », etc. C’est pratique pour le débogage.

2. Interface Runnable : la meilleure approche dans la plupart des cas

Java est un langage qui aime la flexibilité. La classe Thread hérite déjà de Object, et Java ne propose pas d’héritage multiple de classes. Si vous faites hériter votre classe de Thread, vous ne pourrez pas l’étendre d’autre chose (par exemple, si vous avez une classe Car et que vous voulez en faire un thread — il faudra choisir).

La solution : extraire la logique du thread dans un objet séparé qui implémente l’interface Runnable. C’est une interface avec une seule méthode run(). Ensuite, vous créez un objet de la classe Thread, vous lui passez votre Runnable — et vous démarrez le thread.

Exemple 2. Classe avec Runnable

// Classe implémentant Runnable
public class HelloRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Bonjour depuis Runnable ! Thread : " + Thread.currentThread().getName());
    }
}

public class Main {
    public static void main(String[] args) {
        Runnable runnable = new HelloRunnable();
        Thread thread = new Thread(runnable); // on passe le Runnable à Thread
        thread.start();
        System.out.println("Le thread principal termine son travail.");
    }
}

Ici, nous créons la classe HelloRunnable, qui implémente l’interface Runnable. Dans la méthode main, on crée un objet de cette classe et on le passe au constructeur de Thread. Après cela, le thread est démarré avec l’appel à start().

À part, mentionnons la méthode Thread.currentThread(). C’est une méthode statique qui permet d’obtenir l’objet du thread courant et donc de savoir où s’exécute notre code.

Pourquoi est-ce mieux ?

  • Votre classe peut hériter de n’importe quelle autre classe (et pas seulement de Thread).
  • On peut réutiliser le même Runnable pour des lancements dans plusieurs threads.
  • Le code devient plus flexible et plus propre.

3. Syntaxe : classes anonymes et expressions lambda

Java évolue ! Depuis Java 8, on peut écrire encore plus court et plus pratique.

Exemple 3. Classe anonyme

Parfois, on n’a pas envie de créer un fichier séparé pour une classe utilisée une seule fois. On peut la déclarer directement à l’endroit d’usage : c’est ce qu’on appelle une classe anonyme.

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Runnable anonyme ! Thread : " + Thread.currentThread().getName());
            }
        });
        thread.start();
        System.out.println("Le thread principal termine son travail.");
    }
}

Exemple 4. Expression lambda (Java 8+)

L’interface Runnable est fonctionnelle (elle ne contient qu’une seule méthode abstraite). On peut donc utiliser une expression lambda :

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println("Thread lambda ! Thread : " + Thread.currentThread().getName());
        });
        thread.start();
        System.out.println("Le thread principal termine son travail.");
    }
}

En bref :
Runnable est implémenté « sur place », et l’expression lambda fournit l’implémentation de la méthode run().

4. Démarrer plusieurs threads

Vous pouvez démarrer autant de threads que vous voulez (enfin, presque — tout dépend des ressources de la machine).

Exemple 5. Lancer plusieurs threads

public class Main {
    public static void main(String[] args) {
        for (int i = 1; i <= 3; i++) {
            int threadNumber = i; // important : faire une copie de la variable pour la lambda !
            Thread thread = new Thread(() -> {
                System.out.println("Thread #" + threadNumber + ": bonjour!");
            });
            thread.start();
        }
        System.out.println("Le thread principal termine son travail.");
    }
}

La sortie peut varier !
Les threads s’exécutent en parallèle — celui qui affichera son message en premier dépend de l’OS et de l’ordonnanceur de threads.

5. Cycle de vie d’un thread

Comprendre comment vit un thread est important pour le débogage et pour utiliser correctement les threads.

États principaux d’un thread

  • NEW — le thread est créé mais pas encore démarré (new Thread(...)).
  • RUNNABLE — le thread est démarré (start()), il peut s’exécuter.
  • TERMINATED — le thread a terminé son exécution (la méthode run() s’est achevée).

Que se passe-t-il lors de l’appel à start() et run()

  • start() — crée un nouveau thread et appelle sa méthode run() dans ce nouveau thread.
  • run() — une méthode ordinaire ; si on l’appelle directement, elle s’exécute dans le thread courant (aucun nouveau thread n’est créé !).

Exemple :

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> System.out.println("run() depuis le thread: " + Thread.currentThread().getName()));
        thread.run();   // APPELÉ DANS LE THREAD PRINCIPAL !
        thread.start(); // APPELÉ DANS UN THREAD SÉPARÉ !
    }
}

6. Pratique : réalisons une simple application multithread

Poursuivons le développement de notre application d’apprentissage — supposons que nous écrivions un simple système de journalisation où chaque thread écrit son propre message.

public class LoggerTask implements Runnable {
    private final String message;

    public LoggerTask(String message) {
        this.message = message;
    }

    @Override
    public void run() {
        System.out.println("[" + Thread.currentThread().getName() + "] Log : " + message);
    }
}

public class Main {
    public static void main(String[] args) {
        Thread logger1 = new Thread(new LoggerTask("Démarrage du système"));
        Thread logger2 = new Thread(new LoggerTask("Chargement des données"));
        Thread logger3 = new Thread(new LoggerTask("Traitement de la requête"));

        logger1.start();
        logger2.start();
        logger3.start();

        System.out.println("Le thread principal termine son travail.");
    }
}

Que se passe-t-il ?
Trois threads écrivent en parallèle leurs messages dans la console. L’ordre des messages n’est pas garanti — c’est le principe même du multithreading.

7. Subtilités utiles

Tableau : comparaison des façons de créer des threads

Méthode Flexibilité Réutilisation Recommandé ?
Héritage de Thread Faible Non Uniquement pour des exemples simples/pédagogiques
Implémentation de Runnable Élevée Oui Oui
Classe anonyme Moyenne Non Pour des tâches ponctuelles
Expression lambda Élevée Non Oui (Java 8+)

Schéma : comment fonctionne le lancement d’un thread

+----------------------+ 
|  main (thread principal)|
+----------------------+
           |
           v
+----------------------+
|  Thread thread = ... |
+----------------------+
           |
           v
+----------------------+
|  thread.start()      |  ← Le thread démarre
+----------------------+
           |
           v
+----------------------+
|  run() dans un nouveau thread|
+----------------------+
           |
           v
+----------------------+
|  Thread terminé      |
+----------------------+

8. Erreurs typiques lors du lancement des threads

Erreur n° 1 : appeler run() au lieu de start().
Erreur très fréquente chez les débutants : appeler directement la méthode run() sur l’objet thread. Dans ce cas, aucun nouveau thread n’est créé, la méthode s’exécute simplement dans le thread courant (principal). Correct : utilisez toujours start() pour lancer un thread.

Erreur n° 2 : relancer un même thread.
Il est impossible d’appeler start() plus d’une fois sur le même objet Thread : cela déclenchera une IllegalThreadStateException. Si vous devez relancer une tâche, créez un nouvel objet Thread.

Erreur n° 3 : modifier des variables partagées sans synchronisation.
Si plusieurs threads accèdent aux mêmes variables, le résultat peut être inattendu (condition de compétition, « race condition »). Nous parlerons de synchronisation plus tard, mais en attendant — soyez prudent.

Erreur n° 4 : ne pas tenir compte de la fin des threads.
S’il est important d’attendre la fin d’un thread, utilisez la méthode join(). Sinon, le thread principal peut se terminer avant les threads enfants.

Erreur n° 5 : confondre le nom du thread et le nom de la classe.
La méthode getName() renvoie le nom du thread, pas le nom de la classe. Pour le débogage, il est souvent utile de donner explicitement des noms aux threads via le constructeur ou setName().

1
Mission
JAVA 25 SELF, niveau 51, leçon 1
Bloqué
Lancement d'un drone-coursier autonome ✈️
Lancement d'un drone-coursier autonome ✈️
1
Mission
JAVA 25 SELF, niveau 51, leçon 1
Bloqué
Confier une tâche au nouveau stagiaire 🧑‍💻
Confier une tâche au nouveau stagiaire 🧑‍💻
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION