IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

AWT/Swing Java Discussion :

[JPanel] un thread pas très catholique...


Sujet :

AWT/Swing Java

  1. #21
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Mon premier exemple était faux, désolé...

    Voici quelques choses de plus propre pour mettre en pause ou arrêter un thread :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    class ThreadDeDessin extends Thread
    {
        private boolean isPaused = false;
     
        public void run() {
            // Tant que le thread n'est pas interrompue
            try {
                // On vérifie l'état de la pause (méthode bloquante) :
                checkPause();
     
                // On effectue les traitement 
                preparerProchainPoint();
                repaint();
                sleep(1000);
            } catch( InterruptedException e ) {
                // En cas d'InterruptedException on sort de suite :
            }
        }
     
        protected synchronized void checkPause() throws InterruptedException {
            if (isInterrupted()) {
                    // Si le thread est déjà interrompu, on provoque une exception :
                    throw new InterruptedException();
            }
            if (this.isPaused) {
                // Si on est en pause, on met le thread en attente :
                // Le wait() libère le verrou du synchronized :
                this.wait();
            }
        }
     
        public synchronized void setPaused(boolean newValue) {
            isPaused = newValue;
            if (!this.isPaused) {
                // Lorsqu'on enlève la pause, on envoit une notification au thread :
                this.notify();
            }
        }
    }
    • interrupt() permet de stopper le thread.
    • setPaused(true) permet de mettre le thread en pause.
    • setPaused(false) permet de sortir le thread de la pause.


    Explication :

    Le try/catch global permet d'intercepter les InterruptedException dans les cas où l'appel à interrupt() a lieu pendant que le thread est en attente dans wait() ou sleep(). Dans ces cas là le thread est arrêté de suite.

    La méthode checkPause() vérifie si le thread doit se mettre en pause et l'endort avec wait() le cas échéant. On vérifie d'abord l'état de isInterrupted() afin de terminer le thread si besoin...

    La méthode setPaused() permet de modifier l'état de pause du thread. Dans le cas où on sort d'une pause on envoi un notify() afin de réveiller le thread s'il était déjà rentré en sommeil dans wait()...


    La principale différence entre ces méthodes et les méthodes original stop(), suspend() et resume(), c'est que si l'appel survient pendant ton traitement (preparerProchainPoint() ou repaint()), elles n'auront aucune influence sur leurs déroulements (mis à part si tu utilises à l'intérieur d'autres méthodes interruptible...), et la pause/fin du thread n'aura réellement lieux qu'à la fin du traitement.


    Ainsi tu n'a pas un comportment hyper-réactif (la pause n'aura pas forcément lieu instantannément), mais tu évites de te retrouver avec des objets ou des valeurs dans des états incorrects, ce qui pourrait provoquer des erreurs autres part dans ton application...

    a++

  2. #22
    Membre éclairé Avatar de calogerogigante
    Homme Profil pro
    Technicien réseau
    Inscrit en
    Avril 2003
    Messages
    609
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Technicien réseau
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2003
    Messages : 609
    Par défaut
    Merci pour toute cette explication très claire, Adiguba.

    J'ai donc analysé et essayé ta classe telle quelle, mais j'ai maintenant un problème très bizarre : les droites aléatoires ne démarrent plus...

    Seule la 1ère droite est tracée, et puis plus rien : figé !!
    On dirait que la méthode run() n'est pas lancée quand on démarre le thread avec start()...

    Bizarre ? Bizarre ?

    Est-ce que le petit programme fonctionne bien chez toi (avec ta version de la classe ThreadDeDessin) ?

  3. #23
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par calogerogigante
    Seule la 1ère droite est tracée, et puis plus rien : figé !!
    On dirait que la méthode run() n'est pas lancée quand on démarre le thread avec start()...

    Bizarre ? Bizarre ?
    Non... en fait j'ai encore faire une connerie
    J'ai oublié la boucle infini dans la méthode run() (je n'avais pas vraiment testé avec ton code et j'ai enlevé trop de chose...) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     public void run() {
                // Tant que le thread n'est pas interrompue
                try {
                    while(true) {
                        // On vérifie l'état de la pause (méthode bloquante) :
                        checkPause();
                        
                        // On effectue les traitement
                        preparerProchainPoint();
                        repaint();
                        sleep(1000);
                    }
                } catch( InterruptedException e ) {
                    // En cas d'InterruptedException on sort de suite :
                }
            }
    Sinon, étant donné que tu modifies ton composant graphique dans la méthode preparerProchainPoint(), il vaudrait mieux effectuer ce traitement dans l'EDT :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
            public void run() {
                Runnable task = new Runnable() {
                    public void run() {
                        preparerProchainPoint();
                        repaint();
                    }
                };
                // Tant que le thread n'est pas interrompue
                try {
                    while(true) {
                        // On vérifie l'état de la pause (méthode bloquante) :
                        checkPause();
                        
                        // On effectue les traitement
                        SwingUtilities.invokeLater(task);
                        sleep(1000);
                    }
                } catch( InterruptedException e ) {
                    // En cas d'InterruptedException on sort de suite :
                }
            }
    a++

  4. #24
    Membre éclairé Avatar de calogerogigante
    Homme Profil pro
    Technicien réseau
    Inscrit en
    Avril 2003
    Messages
    609
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Technicien réseau
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2003
    Messages : 609
    Par défaut
    Bon, Adiguba, c'est OK. Tout marche très bien maintenant. Grâce à cette façon de faire avec les threads, j'ai pu me passer de devoir gérer l'aspect enabled ou non des 3 boutons. Car la méthode fonctionne dans tous les cas (exemple : pas d'erreur si je fais l'action de mettre en pause le thread quand celui-ci est déjà en pause.)

    J'ai encore une petite question, mais avant, je poste le code de mon traçeur de droites aléatoires, au thread amélioré :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    import java.awt.*;
    import java.awt.event.*;
     
    import javax.swing.*;
     
    public class FenetrePrincipale extends JFrame
                                   implements ActionListener
    {
    public PanneauDeDessin pan = new PanneauDeDessin();
     
    public JButton continu = new JButton("Continuer à tracer");
    public JButton stopper = new JButton("Arrêter");
    public JButton effacer = new JButton("Effacer");
     
    public FenetrePrincipale()
    {
      super("Droites aléatoires");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      getContentPane().add(pan, BorderLayout.CENTER);
      pan.demarrerThread();
      FlowLayout fl = new FlowLayout(FlowLayout.CENTER);
      JPanel sous_pan = new JPanel(fl);
      sous_pan.add(continu); continu.addActionListener(this);
      sous_pan.add(stopper); stopper.addActionListener(this);
      sous_pan.add(effacer); effacer.addActionListener(this);
      getContentPane().add(sous_pan, BorderLayout.SOUTH);
     
      pack();
      setVisible(true);
    }
     
    public void actionPerformed(ActionEvent ev)
    {
    if((JButton)(ev.getSource()) == continu)
      {
      pan.reprendreThread();
      System.out.println("Continuer");
      }
    if((JButton)(ev.getSource()) == stopper)
      {  
      pan.arreterThread();
      System.out.println("Stopper");
      }
    if((JButton)(ev.getSource()) == effacer)
      {
      pan.effacerEcran();
      System.out.println("Effacer");
      }
    }
     
    public static void main(String[] args)
      {
      try
        {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        }
      catch (Exception e)
        {
        System.out.println(e);
        }
      new FenetrePrincipale();
      }
    }
    Et la deuxième classe :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    import javax.swing.*;
    import java.awt.*;
    import java.util.*;
     
    public class PanneauDeDessin extends JPanel
    {
    public ThreadDeDessin thr = new ThreadDeDessin();
     
    int large = 500;
    int longu = 300;
     
    int X1=large/2,
        Y1=longu/2,
        X2=large/2,
        Y2=longu/2;
     
    Graphics buffer;
    Image    image;
     
    public PanneauDeDessin()
      {
      setPreferredSize(new Dimension(large, longu));
      }
     
    public void paintComponent(Graphics g)
      {
      if (buffer==null)
        {
        image  = createImage(large, longu);
        buffer = image.getGraphics();
        buffer.setColor(Color.WHITE);
        buffer.fillRect(0, 0, large, longu);
        }
     
      buffer.setColor( choixDUneCouleur() );
      buffer.drawLine(X1,Y1,X2,Y2);
     
      g.drawImage(image, 0, 0, this);
      }
     
    public void effacerEcran()
    {
      buffer.setColor(Color.WHITE);
      buffer.fillRect(0,0,large,longu);
      buffer.setColor(Color.BLACK);
      repaint();
    }
    public Color choixDUneCouleur()
    {
    Random rand = new Random();
    int R = rand.nextInt(255);
    int G = rand.nextInt(255);
    int B = rand.nextInt(255);
    return new Color(R,G,B);
    }
     
    public void preparerProchainPoint()
      {
      Random rand = new Random();
      X1 = X2;
      Y1 = Y2;
      X2 = rand.nextInt(large);
      Y2 = rand.nextInt(longu);
      }
     
    public void demarrerThread()
      {
      thr.start();
      }
     
    public void arreterThread()
      {
      thr.setPaused(true); 
      }
     
    public void reprendreThread()
      {
      thr.setPaused(false);
      }
     
    class ThreadDeDessin extends Thread
    {
        private boolean isPaused = false;
     
        public void run()
        {
            Runnable task = new Runnable() {
                                           public void run()
                                              {
                                              preparerProchainPoint();
                                              repaint();
                                              }
                                           };
            // Tant que le thread n'est pas interrompu
            try {
                while(true)
                    {
                    // On vérifie l'état de la pause (méthode bloquante) :
                    checkPause();
     
                    // On effectue les traitement
                    SwingUtilities.invokeLater(task);
                    sleep(100);
                    }
                }
            catch( InterruptedException e )
                {
                // En cas d'InterruptedException on sort de suite :
                }
        }
     
        protected synchronized void checkPause() throws InterruptedException
        {
            if (isInterrupted())
            {
                    // Si le thread est déjà interrompu, on provoque une exception :
                    throw new InterruptedException();
            }
            if (this.isPaused)
            {
                // Si on est en pause, on met le thread en attente :
                // Le wait() libère le verrou du synchronized :
                this.wait();
            }
        }
     
        public synchronized void setPaused(boolean newValue)
        {
            isPaused = newValue;
            if (!this.isPaused)
            {
                // Lorsqu'on enlève la pause, on envoit une notification au thread :
                this.notify();
            }
        }
    }
    }
    Ma question : maintenant, si je désirais tuer totalement le thread (pas dans cet exemple mais éventuellement dans un autre programme (càd pour que l'objet thread soit complètement détruit de la mémoire), comment je dois faire ?

    Car la mise en pause, ici, dans la dernière version de ce programme, ne le tue en aucun cas ???

    Je dois faire appel à interrupt() , c'est ça ?

  5. #25
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par calogerogigante
    Ma question : maintenant, si je désirais tuer totalement le thread (pas dans cet exemple mais éventuellement dans un autre programme (càd pour que l'objet thread soit complètement détruit de la mémoire), comment je dois faire ?

    Car la mise en pause, ici, dans la dernière version de ce programme, ne le tue en aucun cas ???

    Je dois faire appel à interrupt() , c'est ça ?
    Oui...

    En fait lorsque tu appelles la méthode interrupt() depuis un autre thread tu as trois grand cas :
    • Le thread est dans une méthode de sommeil (wait(), sleep(), join()), et dans ce cas l'attente est interrompu par une InterruptedException.
    • Le thread est dans une méthode bloquante d'un InterruptibleChannel, et dans ce cas tu obtiens une ClosedByInterruptException...
    • Sinon le thread continue son déroulement normal...
    Mais dans tous les cas le status du thread passe à interrupted...


    Dans ton cas, seul le premier et le dernier cas te concerne (puisque tu n'utilises pas de Channel). Lorsque tu appelles la méthode interrupt() :
    • Si ton thread est dans une des méthodes d'attentes (sleep() ou wait()), alors l'InterruptedException te sort de la boucle infini en rentrant dans le catch. Tu arrive donc à la fin de la méthode run() ce qui met fin à ton thread.
    • Si tu n'est pas dans une des méthodes d'attentes, ton thread continuera normalement jusqu'à ce qu'il arrive dans la méthode checkPause() qui vérifie le status 'interuptible' de ton thread (qui sera obligatoirement renseigné lors de l'appel de interrupted()):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    if (isInterrupted())
    {
        // Si le thread est déjà interrompu, on provoque une exception :
        throw new InterruptedException();
    }
    Donc si la méthode interrupted() a été appellé tu renvois un InterruptedException qui provoquera la fin du thread...


    Donc dans le pire des cas tu effectueras une boucle de trop dans ton thread, mais il s'arrêtera correctement...

    Maintenant si ta boucle de traitement était beaucoup plus longue, il serait mieux de vérifier plusieurs fois l'état interrupted...


    Enfin concernant la mémoire, il faut bien sûr que tu mettes ton attribut thr à null une fois que tu l'as arrêter, pour que le GC puissent le 'nettoyer'...



    Sinon, juste un détail sur ce code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if((JButton)(ev.getSource()) == continu)
    Comme tu compares des références, tu n'as pas besoin de faire un cast, ceci marche parfaitement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if(ev.getSource() == continu)
    Et en plus tu n'as pas de risque de ClassCastException

    a++

  6. #26
    Membre éclairé Avatar de calogerogigante
    Homme Profil pro
    Technicien réseau
    Inscrit en
    Avril 2003
    Messages
    609
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Technicien réseau
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2003
    Messages : 609
    Par défaut
    Merci pour ces précisions finales, adiguba.

    Pour être certain d'avoir compris tout ton développement théorique, je suis maintenant en train de programmer un petit essai avec une interface swing symbolisant le contrôle de 4 voitures. Les voitures peuvent rouler (et donc consomment le nombre de litres d'essence qu'elles ont : c'est un champs integer private), peuvent être mise en pause (arrêt de la consommation), et peuvent avoir un accident (thread tué).

    Tu verras que ma classe voiture étends Thread.

    J'aurais bientôt fini cet exemple perso (pour améliorer mon aisance dans les threads) dans 2 ou 3 jours... Dès qu'il sera fin prêt, je le mettrais ici pour avoir vos avis et en faire profiter tout le monde : et surtout voir si j'ai bien compris enfin toute cette partie importante de JAVA.


    P.S. Pour le cast en (JButton), j'ai fait ça car je me souviens avoir eu des problèmes de compilation sans ce cast (je sais plus exactement lesquels), et dès lors, j'ai attrapé le réflexe. Je referais des tests pour voir.

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 2 PremièrePremière 12

Discussions similaires

  1. [Timer]Timer pas très précis
    Par harold63 dans le forum Collection et Stream
    Réponses: 4
    Dernier message: 25/07/2005, 08h36
  2. [Dessin JPanel] Generer dessin pas a pas
    Par -=Spoon=- dans le forum 2D
    Réponses: 2
    Dernier message: 03/12/2004, 22h41
  3. 2 SELECT Qui ne s'entendent pas très bien entre eux :(
    Par moutanakid dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 05/08/2004, 16h46

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo