Swing et les threads

Le main() et l'EDT

D. Olivier


Université du Havre

Les threads dans une interface graphique swing

Il y a au moins 4 threads utilisés dans une application graphique

  1. Le thread initial : il démarre le main() ;
  2. L'Event Dispatch Thread (EDT) : initialise et gère l'interface graphique et appelle les écouteurs 
  3. Les threads de traitements : lancés par les écouteurs qui effectue un traitement long 
  4. Le garbage collector.

Le thread initial

  • Effectue différentes initialisations (du modèle p.e) ;
  • Lance l'EDT en dernière instruction ;
  • Se termine quand le main() se termine et seul l'EDT continue alors.

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class EDTEtThreadInitial extends JFrame {

   public EDTEtThreadInitial() {
      // A faire
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }
   public static void main(String[] args) {
   	// A Faire : les initialisations
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            EDTEtThreadInitial f = new EDTEtThreadInitial();
            f.setVisible(true);
         }
      });
   }
}
						
EDTEtThreadInitial.java

Thread(s) de traitement

  • Problème :
    • Exécuter un traitement long au niveau de l'interface graphique ;
    • L'interface doit réagir aux interactions.
  • Solution :
    • Executer un thread pour le traitement long.

public class DemoJProgressBar3 extends JFrame {
	JProgressBar barre;

    public DemoJProgressBar3() {
        super("JProgressBar et les threads");
        // Le layout
        setLayout(new BorderLayout());
        // Les éléments de l'interface
        barre = new JProgressBar();
        JButton bouton = new JButton("Charger");
        // On ajoute les éléments
        add(bouton, BorderLayout.CENTER);
        add(barre, BorderLayout.SOUTH);
        // Ajout d'un écouteur sur le bouton
        bouton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
        // Le thread d'exécution de la tache longue     
                Thread t = new Thread() {
                    public void run() {
                        Traitement tache = new Traitement(true);
                        tache.execute(barre);
                    }
                };
                t.start();
            }
        });
        // Gestion fermeture de la fenêtre
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                DemoJProgressBar3 fenetre = new DemoJProgressBar3();
                fenetre.setVisible(true);
            }
        });
    }
}
		
			
Traitement.java
DemoJProgressBar3.java

L'Event Dispatch Thread

Les composants de Swing doivent être manipulés dans l'EDT.

  • L'EDT alterne cycle d'affichage et cycle de propagation des événements.
  • La manipulation de l'EDT se fait par la classe javax.swing.SwingUtilities
    • isEventDispatchThread() permet de vérifier que le traitement se fait dans l'EDT.
    • Lorsqu'un traitement doit modifier l'interface on utilise les méthodes invokeLater() ou invokeAndWait().

Utilisation d'invokeLater()

  • La méthode peut-être utilisée dans n'importe quel thread pour exécuter une méthode run() dans l'EDT ;
  • La méthode run() doit être définie dans un objet respectant l'interface Runnable ;
  • invokeLater() s'exécute de manière asynchrone (non bloquant) ;
  • invokeAndWait() attend la fin de l'exécution de la méthode run(), attention aux interblocages.
                        
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;

public class DemoJProgressBar5 extends JFrame {
     JProgressBar barre;

     public DemoJProgressBar5() {
          super("JProgressBar et les threads");
          // Le layout
          setLayout(new BorderLayout());
          // Les éléments de l'interface
          barre = new JProgressBar();
          final JButton bouton = new JButton("Charger");
          // On ajoute les éléments
          add(bouton, BorderLayout.CENTER);
          add(barre, BorderLayout.SOUTH);
          // Ajout d'un écouteur sur le bouton
          bouton.addActionListener(new ActionListener() {
               public void actionPerformed(ActionEvent e) {
                    Thread t = new Thread() {
                         public void run() {
                              Traitement tache = new Traitement(true);
                              tache.execute(barre);
                              SwingUtilities.invokeLater(new Runnable() {
                                   public void run() {
                                        bouton.setBackground(Color.GRAY); //Dans l'EDT
                                   }
                              });
                         }
                    };
                    t.start();
               }
          });
          // Gestion fermeture de la fenêtre
          setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          pack();
     }

     public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
               public void run() {
                    DemoJProgressBar5 fenetre = new DemoJProgressBar5();
                    fenetre.setVisible(true);
               }
          });
     }
}

                        
                        
Traitement.java
DemoJProgressBar5.java