CodeGym /Cours /JAVA 25 SELF /Thread EDT et opérations longues dans l’UI

Thread EDT et opérations longues dans l’UI

JAVA 25 SELF
Niveau 50 , Leçon 4
Disponible

1. Qu’est-ce que l’EDT (Event Dispatch Thread)

Dans les applications graphiques Java — que ce soit Swing ou JavaFX — toutes les actions de l’utilisateur (clics, touches), ainsi que le redessin des fenêtres, sont traitées dans un thread spécial appelé EDT (Event Dispatch Thread, thread de distribution des événements).

Pourquoi en a‑t‑on besoin ? Les composants d’UI en Java ne sont pas thread-safe. Pour éviter les conditions de course et les artefacts, toutes les modifications de l’interface sont effectuées strictement en un seul endroit — dans l’EDT. C’est comme une caisse avec un seul caissier : un même ticket ne peut pas être traité simultanément par plusieurs personnes.

Dans Swing, l’EDT exécute les gestionnaires d’événements et le redessin des composants (par exemple actionPerformed). Dans JavaFX, l’équivalent est le JavaFX Application Thread, sur lequel s’exécutent les mises à jour de l’UI et des gestionnaires comme setOnAction.

2. Le problème des « opérations longues » dans l’UI

Que se passe‑t‑il si l’on lance une opération longue dans l’EDT ?

Lorsque l’utilisateur appuie sur un bouton, le gestionnaire (par exemple actionPerformed ou setOnAction) s’exécute sur l’EDT. Si vous y lancez une tâche lourde (lecture d’un gros fichier, requête réseau, calculs complexes), toute l’UI « se fige » :

  • La fenêtre cesse de répondre aux clics et aux frappes.
  • Le redessin ne fonctionne plus — lors d’un déplacement, la fenêtre « se fige ».
  • L’utilisateur pense que l’application « a planté ».

Exemple de mauvais code (Swing) :

button.addActionListener(e -> {
    // Opération longue directement dans l’EDT !
    longOperation(); // Par exemple, lecture d’un gros fichier
    label.setText("Terminé!");
});

Résultat : tant que longOperation() s’exécute, la fenêtre ne réagit pas à l’utilisateur.

Pourquoi ? L’EDT traite les tâches en file et ne peut en exécuter qu’une à la fois. Tant qu’il est occupé par votre opération longue, il ne peut traiter ni les clics ni le redessin.

3. Solution : opérations longues — uniquement dans des threads d’arrière‑plan

Principe :

  • Toutes les opérations longues — uniquement dans des threads en arrière-plan.
  • Toutes les modifications de l’UI — uniquement dans l’EDT/JavaFX Application Thread.

Lancer une opération longue dans un thread séparé

Exemple (Swing) :

button.addActionListener(e -> {
    new Thread(() -> {
        longOperation(); // S’exécute dans un thread en arrière-plan
        // Il faut maintenant mettre à jour l’UI — mais uniquement depuis l’EDT !
        SwingUtilities.invokeLater(() -> label.setText("Terminé!"));
    }).start();
});

Exemple (JavaFX) :

button.setOnAction(e -> {
    new Thread(() -> {
        longOperation();
        // Mise à jour de l’UI via Platform.runLater
        Platform.runLater(() -> label.setText("Terminé!"));
    }).start();
});

Comment mettre à jour l’UI depuis un thread en arrière‑plan ?

  • Swing : utilisez SwingUtilities.invokeLater(Runnable) — la tâche sera placée dans la file de l’EDT.
  • JavaFX : utilisez Platform.runLater(Runnable) — la tâche s’exécutera dans le JavaFX Application Thread.

Pourquoi ne pas simplement appeler label.setText(...) depuis un thread en arrière‑plan ? Parce que cela viole la sûreté des threads de l’UI : les composants doivent être modifiés uniquement depuis le thread de l’interface.

Classes dédiées pour les tâches en arrière‑plan

Dans les applications réelles, il faut souvent afficher la progression, permettre l’annulation et gérer les erreurs. Pour cela, il existe :

  • SwingWorker<T, V> — pour Swing ;
  • Task<V>, Service<V> — pour JavaFX.

Exemple (JavaFX Task) :

Task<Void> task = new Task<>() {
    @Override
    protected Void call() throws Exception {
        longOperation();
        // On peut mettre à jour la progression : updateProgress(...)
        return null;
    }
};

task.setOnSucceeded(e -> label.setText("Terminé!"));
task.setOnFailed(e -> label.setText("Erreur!"));

new Thread(task).start();

Avantages : progression, annulation, événements de succès/échec. Mise à jour de l’UI via des méthodes sûres (updateMessage, updateProgress) ou des gestionnaires (setOnSucceeded etc.).

4. Bonnes et mauvaises pratiques

À ne pas faire : opérations longues dans le gestionnaire d’événements

button.setOnAction(e -> longOperation()); // L’UI va se bloquer !

À faire : opérations longues dans un thread séparé

button.setOnAction(e -> new Thread(() -> longOperation()).start());

Encore mieux : utiliser Task/Worker

JavaFX :

button.setOnAction(e -> {
    Task<Void> task = new Task<>() {
        @Override
        protected Void call() throws Exception {
            longOperation();
            return null;
        }
    };
    task.setOnSucceeded(ev -> label.setText("Terminé!"));
    new Thread(task).start();
});

Swing :

button.addActionListener(e -> {
    SwingWorker<Void, Void> worker = new SwingWorker<>() {
        @Override
        protected Void doInBackground() throws Exception {
            longOperation();
            return null;
        }
        @Override
        protected void done() {
            label.setText("Terminé!");
        }
    };
    worker.execute();
});

5. Pratique : exemple avec chargement de fichier

JavaFX :

button.setOnAction(e -> {
    Task<String> task = new Task<>() {
        @Override
        protected String call() throws Exception {
            // Simulation d’un chargement long
            Thread.sleep(2000);
            return "Fichier chargé!";
        }
    };
    task.setOnSucceeded(ev -> label.setText(task.getValue()));
    new Thread(task).start();
});

Swing :

button.addActionListener(e -> {
    SwingWorker<String, Void> worker = new SwingWorker<>() {
        @Override
        protected String doInBackground() throws Exception {
            Thread.sleep(2000);
            return "Fichier chargé!";
        }
        @Override
        protected void done() {
            try {
                label.setText(get());
            } catch (Exception ex) {
                label.setText("Erreur!");
            }
        }
    };
    worker.execute();
});

6. Erreurs typiques avec l’EDT et les opérations longues

Erreur n° 1 : Opération longue dans l’EDT. Toute l’application « se fige », la fenêtre ne répond plus, l’utilisateur pense que le programme a planté.

Erreur n° 2 : Tentative de mise à jour de l’UI depuis un thread en arrière‑plan. Violer la sûreté des threads de l’UI peut entraîner des bugs, des artefacts et des crashs. Utilisez SwingUtilities.invokeLater ou Platform.runLater.

Erreur n° 3 : Absence de gestion d’erreurs dans la tâche en arrière‑plan. Les exceptions « se perdent », l’utilisateur ne sait pas ce qui s’est passé. Dans Swing — redéfinissez done() et lisez get() ; dans JavaFX — abonnez‑vous à setOnFailed.

Erreur n° 4 : Impossibilité d’annuler l’opération longue. L’utilisateur ne peut pas interrompre le chargement/le calcul. Utilisez la prise en charge de l’annulation (SwingWorker.cancel, Task.cancel) et vérifiez les indicateurs d’annulation dans la tâche.

Erreur n° 5 : Pas d’indication de progression. L’utilisateur pense que le programme « s’est figé ». Dans Swing — utilisez la publication de résultats et une barre de progression avec SwingWorker ; dans JavaFX — updateProgress et des indicateurs visuels.

1
Mission
JAVA 25 SELF, niveau 50, leçon 4
Bloqué
Panneau de contrôle du laser gelé 💥
Panneau de contrôle du laser gelé 💥
1
Mission
JAVA 25 SELF, niveau 50, leçon 4
Bloqué
Télécommande laser réactive ✨
Télécommande laser réactive ✨
1
Étude/Quiz
Événements et gestion des événements, niveau 50, leçon 4
Indisponible
Événements et gestion des événements
Événements et gestion des événements
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION