En Java, la classe Thread constitue le fondement de la programmation multithread.
Création de threads (Thread Creation)
- La création de threads peut s'effectuer selon deux approches principales : hériter de la classe Thread ou implémenter l'interface Runnable.
- Approche 1 : Hériter de la classe Thread et surcharger la méthode run()
// Classe personnalisée héritant de Thread
class MonThread extends Thread {
// Surcharge de run() pour définir la logique d'exécution du thread
@Override
public void run() {
System.out.println("Exécution du thread secondaire : " + Thread.currentThread().getName());
}
}
// Utilisation
public class Demo {
public static void main(String[] args) {
MonThread t = new MonThread();
t.start(); // 3. Appel de start() pour lancer le thread (ne pas appeler run() directement)
}
}
- Approche 2 : Implémenter l'interface Runnable et passer l'instance à Thread
// Implémentation de l'interface Runnable
class MonRunnable implements Runnable {
@Override
public void run() {
System.out.println("Exécution du thread secondaire : " + Thread.currentThread().getName());
}
}
// Utilisation
public class Demo {
public static void main(String[] args) {
// Passage de l'instance Runnable à Thread
Thread t = new Thread(new MonRunnable());
t.start(); // Lancement du thread
}
}
Interruption de thread (Thread Interruption)
- L'interruption d'un thread n'est pas un arrêt forcé mais un mécanisme de coopération, qui envoie un signal "veuillez vous arrêter" au thread.
- void interrupt() : Marque le thread comme "interrompu"
- boolean isInterrupted() : Vérifie si le thread est dans l'état interrompu
- Si un thread est interrompu pendant qu'il est en sleep/wait/join, une InterruptedException est levée et l'état d'interruption est effacé
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) { // Vérification de l'état d'interruption
System.out.println("Le thread est en cours d'exécution...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Après capture de l'exception d'interruption, l'état est effacé
System.out.println("Le thread a été interrompu");
Thread.currentThread().interrupt(); // Rétablissement de l'interruption (optionnel)
break;
}
}
});
t.start();
// Le thread principal interrompt le thread secondaire après 1 seconde
Thread.sleep(1000);
t.interrupt();
Jointure de threads (Thread Join)
- Parfois, le thread principal doit attendre la fin d'exécution des threads secondaires avant de continuer, ce qui peut être réalisé avec join().
- t.join() : Le thread courant se met en état de blocage jusqu'à ce que le thread t se termine.
- join(long millis) avec paramètre : Définit un temps d'attente maximal. Si le temps expire avant la fin du thread, le thread courant reprend son exécution.
Thread t = new Thread(() -> {
System.out.println("Début d'exécution du thread secondaire");
try { Thread.sleep(2000); } catch (InterruptedException e) {}
System.out.println("Fin d'exécution du thread secondaire");
});
t.start();
// Le thread principal attend la fin de t (maximum 3 secondes)
t.join(3000);
System.out.println("Le thread principal poursuit son exécution");
Mise en veille de thread (Thread Sleep)
- static void sleep(long millis) : Met en pause le thread courant pendant la durée spécifiée (sans libérer de verrou), lève InterruptedException
System.out.println("Début de la mise en veille");
try {
Thread.sleep(2000); // Mise en veille du thread courant pendant 2 secondes
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Fin de la mise en veille");
Récupération de l'instance de thread (Get Current Instance)
- Lors de l'écriture de code générique (particulièrement dans Runnable), il est souvent nécessaire de savoir quel thread s'exécute.
- Thread.currentThread() : Renvoie une référence à l'objet thread actuellement en cours d'exécution.
- Opérations courantes : obtenir l'ID du thread (getId()), obtenir le nom du thread (getName()), etc.
// Récupération du thread courant (ici le thread main)
Thread threadPrincipal = Thread.currentThread();
System.out.println("Nom du thread courant : " + threadPrincipal.getName()); // Affiche "main"
// Instance de thread secondaire
Thread t = new Thread(() -> {
Thread courant = Thread.currentThread();
System.out.println("Nom du thread secondaire : " + courant.getName()); // Affiche "Thread-0"
});
t.start();
Les différents états des threads Java
Combien d'états existe-t-il pour les threads ?
- Les threads Java comptent 6 états distincts :
- NEW (Nouveau)
- RUNNABLE (Exécutable)
- BLOCKED (Bloqué)
- WAITING (En attente)
- TIMED_WAITING (En attente avec délai)
- TERMINATED (Terminé)
Signification de chaque état et conditions de transition
- NEW (Nouveau)
- Signification : L'objet thread a été créé (new Thread()), mais la méthode start() n'a pas encore été apelée.
- Transition : Après l'appel de start(), le thread passe à l'état RUNNABLE.
- RUNNABLE (Exécutable)
- Signification : Java regroupe les états "prêt (Ready)" et "en cours d'exécution (Running)" du système d'exploitation sous le terme RUNNABLE. Un thread dans cet état peut s'exécuter sur un CPU ou attendre l'allocation d'un quantum de temps par le système d'exploitation.
- Transition :
- Prêt → En cours d'exécution : Obtention d'un quantum de temps CPU.
- En cours d'exécution → Prêt : Épuisement du quantum de temps, ou appel explicite de Thread.yield().
- BLOCKED (Bloqué)
- Signification : Le thread attend l'acquisition d'un verrou exclusif (comme lors de l'entrée dans un bloc/method synchronized), mais ce verrou est actuellement détenu par un autre thread.
- Transition :
- RUNNABLE → BLOCKED : Échec de la tentative d'accès à une zone synchronisée.
- BLOCKED → RUNNABLE : Un autre thread libère le verrou, et le thread courant l'obtient.
- WAITING (En attente)
- Significasion : Le thread est dans un état d'attente indéfini, ne recevant pas de temps CPU, et nécessitant qu'un autre thread le réveille explicitement.
- Transition :
- RUNNABLE → WAITING : Appel de Object.wait() (sans paramètre), Thread.join() (sans paramètre) ou LockSupport.park().
- WAITING → RUNNABLE : Un autre thread appelle Object.notify(), notifyAll() ou LockSupport.unpark().
- TIMED_WAITING (En attente avec délai)
- Signification : Similaire à WAITING, mais le thread se réveille automatiquement après un délai spécifié, sans nécessiter de réveil explicite d'un autre thread.
- Transition :
- RUNNABLE → TIMED_WAITING : Appel de méthodes avec paramètre temporel comme Thread.sleep(ms), Object.wait(ms), Thread.join(ms).
- TIMED_WAITING → RUNNABLE : Fin du délai, ou réveil anticipé (par exemple via notify()).
- TERMINATED (Terminé)
- Significatino : Le thread a terminé son exécution (méthode run() terminée normalement) ou a quitté son exécution à cause d'une exception.
- Transition : Une fois dans cet état, le cycle de vie du thread est terminé et il ne peut pas être redémarré (un nouvel appel à start() lèverait une exception).